internal bool OnHandleRequest(Socket client, string username, string path, NameValueCollection query, int range)
        {
            string photoQuery;
            if (query ["query"] != null)
                photoQuery = query ["query"];
            else
                photoQuery = "";

            int session = 0;
            if (query ["session-id"] != null) {
                session = Int32.Parse (query ["session-id"]);
            }

            /*            if (!sessions.ContainsKey (session) && path != "/server-info" && path != "/content-codes" &&
                path != "/login") {
                ws.WriteResponse (client, HttpStatusCode.Forbidden, "invalid session id");
                return true;
            }
            */
            if (session != 0) {
                sessions [session].LastActionTime = DateTime.Now;
            }

            int clientRev = 0;
            if (query ["revision-number"] != null) {
                clientRev = Int32.Parse (query ["revision-number"]);
            }

            int delta = 0;
            if (query ["delta"] != null) {
                delta = Int32.Parse (query ["delta"]);
            }
            // DEBUG data
            Console.WriteLine ("Before returning resources for path " + path + ", meta " + query ["meta"] + " query " + photoQuery);
            if (dbItemsRegex.IsMatch (path)) //&& photoQuery.Length==0
                Console.WriteLine ("\tThis is a database/items request!");

            if (path == "/server-info") {
                ws.WriteResponse (client, GetServerInfoNode ());
            } else if (path == "/content-codes") {
                ws.WriteResponse (client, ContentCodeBag.Default.ToNode ());
            } else if (path == "/login") {
                ExpireSessions ();

                if (maxUsers > 0 && sessions.Count + 1 > maxUsers) {
                    ws.WriteResponse (client, HttpStatusCode.ServiceUnavailable, "too many users");
                    return true;
                }

                session = random.Next ();
                User user = new User (DateTime.Now, (client.RemoteEndPoint as IPEndPoint).Address, username);

                lock (sessions) {
                    sessions [session] = user;
                }

                ws.WriteResponse (client, GetLoginNode (session));
                OnUserLogin (user);
            } else if (path == "/logout") {
                Console.WriteLine("logout!");
                User user = sessions [session];

                lock (sessions) {
                    sessions.Remove (session);
                }

                ws.WriteResponse (client, HttpStatusCode.OK, new byte [0]);
                OnUserLogout (user);

                return false;
            } else if (path == "/databases") {
                Console.WriteLine ("path==/databases");
                ws.WriteResponse (client, GetDatabasesNode ());
            } else if (dbItemsRegex.IsMatch (path) && photoQuery.Length==0 ){ //&& !dbPhotoRegex.IsMatch (query ["query"])) {
                Console.WriteLine ("dbItemsRegex, query=" + query ["query"] + " meta=" + query ["meta"]);
                int dbid = Int32.Parse (dbItemsRegex.Match (path).Groups [1].Value);

                Database curdb = revmgr.GetDatabase (clientRev, dbid);

                if (curdb == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid database id");
                    return true;
                }

                ArrayList deletedIds = new ArrayList ();

                if (delta > 0) {
                    Database olddb = revmgr.GetDatabase (clientRev - delta, dbid);

                    if (olddb != null) {
                        foreach (Photo photo in olddb.Photos) {
                            if (curdb.LookupPhotoById (photo.Id) == null)
                                deletedIds.Add (photo.Id);
                        }
                    }
                }

                ContentNode node = curdb.ToPhotosNode (query ["meta"].Split (','),
                                                      (int []) deletedIds.ToArray (typeof (int)));
                ws.WriteResponse (client, node);
            } else if (dbPhotoRegex.IsMatch (photoQuery)) {
                Console.WriteLine ("dbPhotoRegex");
                Console.WriteLine ("dbPhotosRegex, query=" + query ["query"] + " meta=" + query ["meta"]);
                string [] photoIds = query ["query"].Split (',');
                Match match = dbPhotoRegex0.Match (path);
                int dbid = Int32.Parse (match.Groups [1].Value);
                int photoid = 0;

                //match = dbPhotoRegex.Match (photoQuery);

                Database db = revmgr.GetDatabase (clientRev, dbid);
                if (db == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid database id");
                    return true;
                }
                ArrayList photoNodes = new ArrayList ();
                Photo photo = db.LookupPhotoById (1);

                foreach (string photoId in photoIds) {
                    match = dbPhotoRegex.Match (photoId);
                    photoid = Int32.Parse (match.Groups [1].Value);
                    photo = db.LookupPhotoById (photoid);
                    photoNodes.Add (photo.ToFileData (query ["meta"].Contains ("dpap.thumb")));
                    // DEBUG
                    //Console.WriteLine ("Requested photo id=" + photoid);
                }

                ArrayList children = new ArrayList ();
                children.Add (new ContentNode ("dmap.status", 200));
                children.Add (new ContentNode ("dmap.updatetype", (byte) 0));
                children.Add (new ContentNode ("dmap.specifiedtotalcount",  2));
                children.Add (new ContentNode ("dmap.returnedcount", 2));
                children.Add (new ContentNode ("dmap.listing", photoNodes));
                ContentNode dbsongs = new ContentNode ("dpap.databasesongs", children);

                if (photo == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid photo id");
                    return true;
                }

                try {
                    try {
                        if (PhotoRequested != null)
                            PhotoRequested (this, new PhotoRequestedArgs (username,
                                                                        (client.RemoteEndPoint as IPEndPoint).Address,
                                                                        db, photo));
                    } catch {}

                    if (photo.FileName != null) {
                        // DEBUG
                        //Console.WriteLine ("photo.Filename != null" + query ["meta"].Split (',') [0]);
                        //ContentNode node = photo.ToFileData ();
                        //node.Dump ();

                        ws.WriteResponse (client, dbsongs);

                    } else if (db.Client != null) {
                        Console.WriteLine ("db.Client != null");
                        long photoLength = 0;
                        Stream photoStream = db.StreamPhoto (photo, out photoLength);

                        try {
                            ws.WriteResponseStream (client, photoStream, photoLength);
                        } catch (IOException) {
                            Console.WriteLine("IOException!");
                        }
                    } else {
                        Console.WriteLine ("Else - internal error");
                        ws.WriteResponse (client, HttpStatusCode.InternalServerError, "no file");
                    }
                } finally {
                    // commented out because it breaks the connection after sending a hires photo
                    // client.Close()
                }
            } else if (dbContainersRegex.IsMatch (path)) {
                int dbid = Int32.Parse (dbContainersRegex.Match (path).Groups [1].Value);

                Database db = revmgr.GetDatabase (clientRev, dbid);
                if (db == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid database id");
                    return true;
                }

                ws.WriteResponse (client, db.ToAlbumsNode ());
            } else if (dbContainerItemsRegex.IsMatch (path)) {
                // DEBUG
                Console.WriteLine ("ContainerItems ! path=" + path);

                Match match = dbContainerItemsRegex.Match (path);
                int dbid = Int32.Parse (match.Groups [1].Value);
                int plid = Int32.Parse (match.Groups [2].Value);

                Database curdb = revmgr.GetDatabase (clientRev, dbid);
                if (curdb == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid database id");
                    return true;
                }

                Album curpl = curdb.LookupAlbumById (plid);
                if (curdb == null) {
                    ws.WriteResponse (client, HttpStatusCode.BadRequest, "invalid playlist id");
                    return true;
                }
                // DEBUG
                Console.WriteLine("db and album ready!");
                ArrayList deletedIds = new ArrayList ();
                if (delta > 0) {
                    Database olddb = revmgr.GetDatabase (clientRev - delta, dbid);

                    if (olddb != null) {
                        Album oldpl = olddb.LookupAlbumById (plid);

                        if (oldpl != null) {
                            IList<Photo> oldplPhotos = oldpl.Photos;
                            for (int i = 0; i < oldplPhotos.Count; i++) {
                                int id = oldpl.GetContainerId (i);
                                if (curpl.LookupIndexByContainerId (id) < 0) {
                                    deletedIds.Add (id);
                                }
                            }
                        }
                    }
                }
                Console.WriteLine("About to send response... meta=" + query["meta"]);
                curpl.ToPhotosNode (query ["meta"].Split (',')).Dump ();

                ws.WriteResponse (client, curpl.ToPhotosNode (query ["meta"].Split (',')));
                //, (int []) deletedIds.ToArray (typeof (int))));
            } else if (path == "/update") {
                int retrev;

                lock (revmgr) {
                    // if they have the current revision, wait for a change
                    if (clientRev == revmgr.Current) {
                        Monitor.Wait (revmgr);
                    }

                    retrev = revmgr.Current;
                }

                if (!running) {
                    ws.WriteResponse (client, HttpStatusCode.NotFound, "server has been stopped");
                } else {
                    ws.WriteResponse (client, GetUpdateNode (retrev));
                }
            } else {
                ws.WriteResponse (client, HttpStatusCode.Forbidden, "GO AWAY");
            }

            return true;
        }
 private void OnUserLogout(User user)
 {
     UserHandler handler = UserLogout;
     if (handler != null) {
         try {
             handler (this, new UserArgs (user));
         } catch (Exception e) {
             Console.Error.WriteLine ("Exception in UserLogout event handler: " + e);
         }
     }
 }
 public UserArgs(User user)
 {
     this.user = user;
 }