Пример #1
0
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Read out settings
            if (uri.Action == "read" || uri.Action == null)
            {
                // If no parameter provided, return settings
                processor.WriteJson(new SettingsResponse(null, Injection.Kernel.Get<IServerSettings>().SettingsModel));
                return;
            }

            // Check for required JSON parameter
            if (!uri.Parameters.ContainsKey("json"))
            {
                processor.WriteJson(new SettingsResponse("Missing required parameter 'json'", null));
                return;
            }

            // Update settings
            if (uri.Action == "update")
            {
                // Take in settings in the JSON format (same as it is stored on disk),
                // pass it on to the Settings class for processing
                string json = HttpUtility.UrlDecode(uri.Parameters["json"]);

                // Attempt to write settings
                bool success = false;
                try
                {
                    success = Injection.Kernel.Get<IServerSettings>().WriteSettings(json);
                    Injection.Kernel.Get<IServerSettings>().Reload();
                }
                catch (JsonException)
                {
                    // Failure if invalid JSON provided
                    processor.WriteJson(new SettingsResponse("Invalid JSON", null));
                    return;
                }

                // If settings fail to write, report error
                if (!success)
                {
                    processor.WriteJson(new SettingsResponse("Settings could not be changed", null));
                    return;
                }

                // If settings wrote successfully, return success
                processor.WriteJson(new SettingsResponse(null, Injection.Kernel.Get<IServerSettings>().SettingsModel));
                return;
            }

            // Invalid action
            processor.WriteJson(new SettingsResponse("Invalid action", null));
            return;
        }
Пример #2
0
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Read out settings
            if (uri.Action == "read" || uri.Action == null)
            {
                // If no parameter provided, return settings
                processor.WriteJson(new SettingsResponse(null, Injection.Kernel.Get <IServerSettings>().SettingsModel));
                return;
            }

            // Check for required JSON parameter
            if (!uri.Parameters.ContainsKey("json"))
            {
                processor.WriteJson(new SettingsResponse("Missing required parameter 'json'", null));
                return;
            }

            // Update settings
            if (uri.Action == "update")
            {
                // Take in settings in the JSON format (same as it is stored on disk),
                // pass it on to the Settings class for processing
                string json = HttpUtility.UrlDecode(uri.Parameters["json"]);

                // Attempt to write settings
                bool success = false;
                try
                {
                    success = Injection.Kernel.Get <IServerSettings>().WriteSettings(json);
                    Injection.Kernel.Get <IServerSettings>().Reload();
                }
                catch (JsonException)
                {
                    // Failure if invalid JSON provided
                    processor.WriteJson(new SettingsResponse("Invalid JSON", null));
                    return;
                }

                // If settings fail to write, report error
                if (!success)
                {
                    processor.WriteJson(new SettingsResponse("Settings could not be changed", null));
                    return;
                }

                // If settings wrote successfully, return success
                processor.WriteJson(new SettingsResponse(null, Injection.Kernel.Get <IServerSettings>().SettingsModel));
                return;
            }

            // Invalid action
            processor.WriteJson(new SettingsResponse("Invalid action", null));
            return;
        }
Пример #3
0
        /// <summary>
        /// Attempt to authenticate a user using URI parameters
        /// </summary>
        public User AuthenticateUri(UriWrapper uri)
        {
            // Attempt to parse various parameters from the URI
            string sessionId = null;
            string username = null;
            string password = null;
            string clientName = null;

            try
            {
                uri.Parameters.TryGetValue("s", out sessionId);
                uri.Parameters.TryGetValue("u", out username);
                uri.Parameters.TryGetValue("p", out password);
                uri.Parameters.TryGetValue("c", out clientName);
            }
            catch
            {
            }

            // If logging in, we are creating a new session
            if (uri.ApiAction == "login")
            {
                // Must use username and password, and create a session
                User user = Injection.Kernel.Get<IUserRepository>().UserForName(username);

                // Validate User ID, and ensure successful session creation
                if (user.UserId != null && user.CreateSession(password, clientName))
                {
                    return user;
                }
            }

            // Otherwise, check for a session key parameter
            if (sessionId != null)
            {
                // This will return the user on success, or null on failure
                return this.AuthenticateSession(sessionId);
            }

            // On failure or no session, return null
            return null;
        }
Пример #4
0
        /// <summary>
        /// Attempt to authenticate a user using URI parameters
        /// </summary>
        public User AuthenticateUri(UriWrapper uri)
        {
            // Attempt to parse various parameters from the URI
            string sessionId  = null;
            string username   = null;
            string password   = null;
            string clientName = null;

            try
            {
                uri.Parameters.TryGetValue("s", out sessionId);
                uri.Parameters.TryGetValue("u", out username);
                uri.Parameters.TryGetValue("p", out password);
                uri.Parameters.TryGetValue("c", out clientName);
            }
            catch
            {
            }

            // If logging in, we are creating a new session
            if (uri.ApiAction == "login")
            {
                // Must use username and password, and create a session
                User user = Injection.Kernel.Get <IUserRepository>().UserForName(username);

                // Validate User ID, and ensure successful session creation
                if (user.UserId != null && user.CreateSession(password, clientName))
                {
                    return(user);
                }
            }

            // Otherwise, check for a session key parameter
            if (sessionId != null)
            {
                // This will return the user on success, or null on failure
                return(this.AuthenticateSession(sessionId));
            }

            // On failure or no session, return null
            return(null);
        }
Пример #5
0
        /// <summary>
        /// Process records play stats for artists, albums, songs
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Ensure an event is present
            if (!uri.Parameters.ContainsKey("event"))
            {
                processor.WriteJson(new StatsResponse("Please specify an event parameter with comma separated list of events"));
                return;
            }

            // Split events into id, stat type, UNIX timestamp triples
            string[] events = uri.Parameters["event"].Split(',');

            // Ensure data sent
            if (events.Length < 1)
            {
                // Report empty data list
                processor.WriteJson(new StatsResponse("Event data list is empty"));
                return;
            }

            // Ensure events are in triples
            if (events.Length % 3 != 0)
            {
                processor.WriteJson(new StatsResponse("Event data list must be in comma-separated triples of form itemId,statType,timestamp"));
                return;
            }

            // Iterate all events
            for (int i = 0; i <= events.Length - 3; i += 3)
            {
                // Store item ID, stat type, and UNIX timestamp
                string itemId = events[i];
                string statType = events[i+1];
                string timeStamp = events[i+2];

                // Initialize to null defaults
                int itemIdInt = -1;
                StatType statTypeEnum = StatType.Unknown;
                long timeStampLong = -1;

                // Perform three checks for valid item ID, stat type, UNIX timestamp
                bool success = Int32.TryParse(itemId, out itemIdInt);
                if (success)
                {
                    success = Enum.TryParse<StatType>(statType, true, out statTypeEnum);
                }
                if (success)
                {
                    success = Int64.TryParse(timeStamp, out timeStampLong);
                }
                if (success)
                {
                    // If all three are successful, generate an item type from the ID
                    ItemType itemType = Injection.Kernel.Get<IItemRepository>().ItemTypeForItemId(itemIdInt);

                    // Case: type is song, stat is playcount
                    if ((itemType == ItemType.Song) && (statTypeEnum == StatType.PLAYED))
                    {
                        // Also record a play for the artist, album, and folder
                        Song song = Injection.Kernel.Get<ISongRepository>().SongForId(itemIdInt);

                        // Trigger now playing service if available
                        NowPlayingService nowPlaying = (NowPlayingService)ServiceManager.GetInstance("nowplaying");
                        if (nowPlaying != null)
                        {
                            nowPlaying.Register(user, song, timeStampLong);
                        }

                        if ((object)song.AlbumId != null)
                        {
                            Injection.Kernel.Get<IStatRepository>().RecordStat((int)song.AlbumId, statTypeEnum, timeStampLong);
                        }
                        if ((object)song.ArtistId != null)
                        {
                            Injection.Kernel.Get<IStatRepository>().RecordStat((int)song.ArtistId, statTypeEnum, timeStampLong);
                        }
                        if ((object)song.FolderId != null)
                        {
                            Injection.Kernel.Get<IStatRepository>().RecordStat((int)song.FolderId, statTypeEnum, timeStampLong);
                        }
                    }

                    // Record stats for the generic item
                    Injection.Kernel.Get<IStatRepository>().RecordStat(itemIdInt, statTypeEnum, timeStampLong);
                }
            }

            // After all stat iterations, return a successful response
            processor.WriteJson(new StatsResponse(null));
        }
Пример #6
0
        /// <summary>
        /// Process records play stats for artists, albums, songs
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Ensure an event is present
            if (!uri.Parameters.ContainsKey("event"))
            {
                processor.WriteJson(new StatsResponse("Please specify an event parameter with comma separated list of events"));
                return;
            }

            // Split events into id, stat type, UNIX timestamp triples
            string[] events = uri.Parameters["event"].Split(',');

            // Ensure data sent
            if (events.Length < 1)
            {
                // Report empty data list
                processor.WriteJson(new StatsResponse("Event data list is empty"));
                return;
            }

            // Ensure events are in triples
            if (events.Length % 3 != 0)
            {
                processor.WriteJson(new StatsResponse("Event data list must be in comma-separated triples of form itemId,statType,timestamp"));
                return;
            }

            // Iterate all events
            for (int i = 0; i <= events.Length - 3; i += 3)
            {
                // Store item ID, stat type, and UNIX timestamp
                string itemId    = events[i];
                string statType  = events[i + 1];
                string timeStamp = events[i + 2];

                // Initialize to null defaults
                int      itemIdInt     = -1;
                StatType statTypeEnum  = StatType.Unknown;
                long     timeStampLong = -1;

                // Perform three checks for valid item ID, stat type, UNIX timestamp
                bool success = Int32.TryParse(itemId, out itemIdInt);
                if (success)
                {
                    success = Enum.TryParse <StatType>(statType, true, out statTypeEnum);
                }
                if (success)
                {
                    success = Int64.TryParse(timeStamp, out timeStampLong);
                }
                if (success)
                {
                    // If all three are successful, generate an item type from the ID
                    ItemType itemType = Injection.Kernel.Get <IItemRepository>().ItemTypeForItemId(itemIdInt);

                    // Case: type is song, stat is playcount
                    if ((itemType == ItemType.Song) && (statTypeEnum == StatType.PLAYED))
                    {
                        // Also record a play for the artist, album, and folder
                        Song song = Injection.Kernel.Get <ISongRepository>().SongForId(itemIdInt);

                        // Trigger now playing service if available
                        NowPlayingService nowPlaying = (NowPlayingService)ServiceManager.GetInstance("nowplaying");
                        if (nowPlaying != null)
                        {
                            nowPlaying.Register(user, song, timeStampLong);
                        }

                        if ((object)song.AlbumId != null)
                        {
                            Injection.Kernel.Get <IStatRepository>().RecordStat((int)song.AlbumId, statTypeEnum, timeStampLong);
                        }
                        if ((object)song.ArtistId != null)
                        {
                            Injection.Kernel.Get <IStatRepository>().RecordStat((int)song.ArtistId, statTypeEnum, timeStampLong);
                        }
                        if ((object)song.FolderId != null)
                        {
                            Injection.Kernel.Get <IStatRepository>().RecordStat((int)song.FolderId, statTypeEnum, timeStampLong);
                        }
                    }

                    // Record stats for the generic item
                    Injection.Kernel.Get <IStatRepository>().RecordStat(itemIdInt, statTypeEnum, timeStampLong);
                }
            }

            // After all stat iterations, return a successful response
            processor.WriteJson(new StatsResponse(null));
        }
Пример #7
0
        // Process API calls based on the class's HTTP URL
        private void ApiProcess()
        {
            // The API request wrapper
            UriWrapper uri = new UriWrapper(this.HttpUrl, this.HttpMethod);

            // The user who is accessing the API
            User apiUser = null;

            // The handler being accessed
            IApiHandler api = null;

            // No API request found?  Serve web UI
            if (!uri.IsApiCall)
            {
                api = Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler("web");
                api.Process(uri, this, apiUser);
                return;
            }

            // Get client IP address
            string ip = ((IPEndPoint)this.Socket.Client.RemoteEndPoint).Address.ToString();

            // Check for valid API action ("web" and "error" are technically valid, but can't be used in this way)
            if (uri.ApiAction == null || uri.ApiAction == "web" || uri.ApiAction == "error")
            {
                ErrorApiHandler errorApi = (ErrorApiHandler)Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler("error");
                errorApi.Process(uri, this, apiUser, "Invalid API call");
                logger.IfInfo(String.Format("[{0}] API: {1}", ip, this.HttpUrl));
                return;
            }

            // Check for session cookie authentication, unless this is a login request
            string sessionId = null;
            if (uri.ApiAction != "login")
            {
                sessionId = this.GetSessionCookie();
                apiUser = Injection.Kernel.Get<IApiAuthenticate>().AuthenticateSession(sessionId);
            }

            // If no cookie, try parameter authentication
            if (apiUser == null)
            {
                apiUser = Injection.Kernel.Get<IApiAuthenticate>().AuthenticateUri(uri);

                // If user still null, failed authentication, so serve error
                if (apiUser == null)
                {
                    ErrorApiHandler errorApi = (ErrorApiHandler)Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler("error");
                    errorApi.Process(uri, this, apiUser, "Authentication failed");
                    logger.IfInfo(String.Format("[{0}] API: {1}", ip, this.HttpUrl));
                    return;
                }
            }

            // apiUser.SessionId will be generated on new login, so that takes precedence for new session cookie
            apiUser.SessionId  = apiUser.SessionId ?? sessionId;
            this.SetSessionCookie(apiUser.SessionId);

            // Store user's current session object
            apiUser.CurrentSession = Injection.Kernel.Get<ISessionRepository>().SessionForSessionId(apiUser.SessionId);

            // Retrieve the requested API handler by its action
            IApiHandler apiHandler = Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler(uri.ApiAction);

            // Check for valid API action
            if (apiHandler == null)
            {
                ErrorApiHandler errorApi = (ErrorApiHandler)Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler("error");
                errorApi.Process(uri, this, apiUser, "Invalid API call");
                logger.IfInfo(String.Format("[{0}] API: {1}", ip, this.HttpUrl));
                return;
            }

            // Log API call
            logger.IfInfo(String.Format("[{0}/{1}@{2}] API: {3} {4}", apiUser.UserName, apiUser.CurrentSession.ClientName, ip, this.HttpMethod, this.HttpUrl));

            // Check if user has appropriate permissions for this action on this API handler
            if (!apiHandler.CheckPermission(apiUser, uri.Action))
            {
                ErrorApiHandler errorApi = (ErrorApiHandler)Injection.Kernel.Get<IApiHandlerFactory>().CreateApiHandler("error");
                errorApi.Process(uri, this, apiUser, "Permission denied");
                return;
            }

            // Finally, process and return results
            apiHandler.Process(uri, this, apiUser);
        }
Пример #8
0
        /// <summary>
        /// Process a Last.fm API request
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // Create Last.fm object for this user
            Lastfm lfm = new Lastfm(user);

            // Pull URL parameters for Last.fm integration
            string eve = null;
            uri.Parameters.TryGetValue("event", out eve);

            if (uri.Action == null || uri.Action == "auth")
            {
                // If not authenticated, pass back authorization URL
                if (!lfm.SessionAuthenticated)
                {
                    processor.WriteJson(new ScrobbleResponse(null, lfm.AuthUrl));
                }
                else
                {
                    // Else, already authenticated
                    processor.WriteJson(new ScrobbleResponse("LFMAlreadyAuthenticated"));
                }
                return;
            }

            // If Last.fm is not authenticated, provide an authorization URL
            if (!lfm.SessionAuthenticated)
            {
                logger.IfInfo("You must authenticate before you can scrobble.");

                processor.WriteJson(new ScrobbleResponse("LFMNotAuthenticated", lfm.AuthUrl));
                return;
            }

            // Create list of scrobble data
            IList<LfmScrobbleData> scrobbles = new List<LfmScrobbleData>();

            // Get Last.fm API enumerations
            LfmScrobbleType scrobbleType = Lastfm.ScrobbleTypeForString(uri.Action);

            // On invalid scrobble type, return error JSON
            if (scrobbleType == LfmScrobbleType.INVALID)
            {
                processor.WriteJson(new ScrobbleResponse("LFMInvalidScrobbleType"));
                return;
            }

            // On now playing scrobble type
            if (scrobbleType == LfmScrobbleType.NOWPLAYING)
            {
                // Ensure ID specified for scrobble
                if (uri.Id == null)
                {
                    processor.WriteJson(new ScrobbleResponse("LFMNoIdSpecifiedForNowPlaying"));
                    return;
                }

                // Add successful scrobble to list, submit
                scrobbles.Add(new LfmScrobbleData((int)uri.Id, null));
                lfm.Scrobble(scrobbles, scrobbleType);
            }
            // Else, unknown scrobble event
            else
            {
                // On null event, return error JSON
                if (eve == null)
                {
                    processor.WriteJson(new ScrobbleResponse("LFMNoEventSpecifiedForScrobble"));
                    return;
                }

                // Ensure input is a comma-separated pair
                string[] input = eve.Split(',');
                if ((input.Length % 2) != 0)
                {
                    processor.WriteJson(new ScrobbleResponse("LFMInvalidInput"));
                    return;
                }

                // Add scrobbles from input data pairs
                int i = 0;
                while (i < input.Length)
                {
                    scrobbles.Add(new LfmScrobbleData(int.Parse(input[i]), long.Parse(input[i + 1])));
                    i = i + 2;
                }
            }

            // Scrobble all plays
            string result = lfm.Scrobble(scrobbles, scrobbleType);
            dynamic resp = null;

            // No response, service must be offline
            if (result == null)
            {
                processor.WriteJson(new ScrobbleResponse("LFMServiceOffline"));
                return;
            }

            // If result is not null, store deserialize and store it
            try
            {
                resp = JsonConvert.DeserializeObject(result);
            }
            catch (Exception e)
            {
                logger.Error(e);
            }

            // Check for nowplaying or scrobbles fields
            if ((resp.nowplaying != null) || (resp.scrobbles != null))
            {
                // Write blank scrobble response
                processor.WriteJson(new ScrobbleResponse());
                return;
            }
            // Write error JSON if it exists
            else if (resp.error != null)
            {
                processor.WriteJson(new ScrobbleResponse(string.Format("LFM{0}: {1}", resp.error, resp.message)));
                return;
            }
        }
Пример #9
0
        /// <summary>
        /// Process returns a serialized list of albums and songs in JSON format
        /// </summary>
        public void Process(UriWrapper uri, IHttpProcessor processor, User user)
        {
            // List of songs and albums to be returned via handler
            IList<Song> songs = new List<Song>();
            IList<Album> albums = new List<Album>();
            PairList<string, int> sectionPositions = new PairList<string, int>();

            // Check if an ID was passed
            if (uri.Id != null)
            {
                // Add album by ID to the list
                Album a = Injection.Kernel.Get<IAlbumRepository>().AlbumForId((int)uri.Id);
                albums.Add(a);

                // Add album's songs to response
                songs = a.ListOfSongs();
            }
            // Check for a request for range of songs
            else if (uri.Parameters.ContainsKey("range"))
            {
                string[] range = uri.Parameters["range"].Split(',');

                // Ensure valid range was parsed
                if (range.Length != 2)
                {
                    processor.WriteJson(new AlbumsResponse("Parameter 'range' requires a valid, comma-separated character tuple", null, null, null));
                    return;
                }

                // Validate as characters
                char start, end;
                if (!Char.TryParse(range[0], out start) || !Char.TryParse(range[1], out end))
                {
                    processor.WriteJson(new AlbumsResponse("Parameter 'range' requires characters which are single alphanumeric values", null, null, null));
                    return;
                }

                // Grab range of albums
                albums = Injection.Kernel.Get<IAlbumRepository>().RangeAlbums(start, end);
            }

            // Check for a request to limit/paginate songs, like SQL
            // Note: can be combined with range or all albums
            if (uri.Parameters.ContainsKey("limit") && uri.Id == null)
            {
                string[] limit = uri.Parameters["limit"].Split(',');

                // Ensure valid limit was parsed
                if (limit.Length < 1 || limit.Length > 2 )
                {
                    processor.WriteJson(new AlbumsResponse("Parameter 'limit' requires a single integer, or a valid, comma-separated integer tuple", null, null, null));
                    return;
                }

                // Validate as integers
                int index = 0;
                int duration = Int32.MinValue;
                if (!Int32.TryParse(limit[0], out index))
                {
                    processor.WriteJson(new AlbumsResponse("Parameter 'limit' requires a valid integer start index", null, null, null));
                    return;
                }

                // Ensure positive index
                if (index < 0)
                {
                    processor.WriteJson(new AlbumsResponse("Parameter 'limit' requires a non-negative integer start index", null, null, null));

                    return;
                }

                // Check for duration
                if (limit.Length == 2)
                {
                    if (!Int32.TryParse(limit[1], out duration))
                    {
                        processor.WriteJson(new AlbumsResponse("Parameter 'limit' requires a valid integer duration", null, null, null));

                        return;
                    }

                    // Ensure positive duration
                    if (duration < 0)
                    {
                        processor.WriteJson(new AlbumsResponse("Parameter 'limit' requires a non-negative integer duration", null, null, null));

                        return;
                    }
                }

                // Check if results list already populated by range
                if (albums.Count > 0)
                {
                    // No duration?  Return just specified number of albums
                    if (duration == Int32.MinValue)
                    {
                        albums = albums.Skip(0).Take(index).ToList();
                    }
                    else
                    {
                        // Else, return albums starting at index, up to count duration
                        albums = albums.Skip(index).Take(duration).ToList();
                    }
                }
                else
                {
                    // If no albums in list, grab directly using model method
                    albums = Injection.Kernel.Get<IAlbumRepository>().LimitAlbums(index, duration);
                }
            }

            // Finally, if no albums already in list, send the whole list
            if (albums.Count == 0)
            {
                albums = Injection.Kernel.Get<IAlbumRepository>().AllAlbums();
                sectionPositions = Utility.SectionPositionsFromSortedList(new List<IGroupingItem>(albums.Select(c => (IGroupingItem)c)));
            }

            // Send it!
            processor.WriteJson(new AlbumsResponse(null, albums, songs, sectionPositions));
        }