1 ///
2 /// Essential data structures and support.
3 /// @file       fundamentals.h - pianod project
4 /// @author     Perette Barella
5 /// @date       2012-03-10
6 /// @copyright  Copyright 2012–2017 Devious Fish. All rights reserved.
7 ///
8 
9 #ifndef __pianod__fundamentals__
10 #define __pianod__fundamentals__
11 
12 #include <config.h>
13 
14 #include <ctime>
15 #include <cassert>
16 #include <cstring>
17 
18 #include <string>
19 #include <vector>
20 #include <stdexcept>
21 
22 /** kept_assert: An assert with important code inside it.
23     In debugging mode, the result must be true/non-0.
24     In release mode, the result is unchecked but the
25     expression is still evaluated. */
26 #ifdef NDEBUG
27 #define kept_assert(x) (x)
28 #else
29 #define kept_assert(x) assert(x)
30 #endif
31 
32 // Works out to 2038 in 4-byte time, surely after the apocolypse for 8-byte
33 #define FAR_FUTURE ((time_t) (((time_t) 0x7fffffff) << ((sizeof (time_t) - 4) * 8)))
34 #define A_LONG_TIME (999999)
35 
36 enum class SearchRange {
37     EXHAUSTIVE,     ///< Get everything matching, and all their contents.
38     SHALLOW,        ///< Get matching things, but *not* their contents.
39     KNOWN,          ///< Get whatever we know about; don't perform external searches.
40     REQUESTABLE,    ///< Search requestable sources for matches; return all items matching.
41     REQUESTS        ///< Search requestable sources for matches, but *not* their contents.
42 };
forRequest(SearchRange range)43 inline bool forRequest (SearchRange range) {
44     return (range == SearchRange::REQUESTABLE || range == SearchRange::REQUESTS);
45 }
deepSearch(SearchRange range)46 inline bool deepSearch (SearchRange range) {
47     return (range == SearchRange::EXHAUSTIVE ||
48             range == SearchRange::REQUESTABLE);
49 }
50 
51 
52 /// Audio output device & driver parameters
53 typedef struct AudioSettings_t {
54     /// Specify which output library for pianod to use.  If empty, uses default.
55     std::string output_library;
56     /// Specify output driver, for use by audio output library.
57     std::string output_driver;
58     /// Specify a specific output device, if there are multiple instances or
59     /// channels.  Precise meaning is up to audio output library.
60     std::string output_device;
61     /// For use by audio output library.  Numeric but stored as string to accommodate unset value.
62     std::string output_id;
63     /// For use by audio output library.
64     std::string output_options;
65     /// For output to network services such as IceCast, the target server info.
66     std::string output_server;
67     /// Initial volume level, managed by pianod.
68     int volume;
69     /// @internal Range of adjustment to use when crossfading, in dB.
70     float crossfade_level = 6.0f;
71     /// @internal Duration of crossfade, in seconds, or 0 to disable crossfading.
72     float crossfade_time = 0.0f;
73     /// @internal Number of seconds to preroll next song, allowing buffering before starting playback.
74     float preroll_time = 5.0f;
75 } AudioSettings;
76 
77 // This should go home to response once that's refactored
78 typedef enum server_status_t {
79     // Informational messages, occur anytime
80     // Playback status
81     V_PLAYING = 1, // 101-109 Playback status
82     V_PAUSED = 2,
83     V_STALLED = 3,
84     V_TRACK_COMPLETE = 4,
85     V_BETWEEN_TRACKS = 5,
86     V_IDLE = 6,
87     V_QUEUE_STOPPED = 7,
88     V_QUEUE_REQUEST = 8,
89     V_QUEUE_RANDOM = 9,
90     // Selected stuff status
91     V_SELECTEDSOURCE = 11,
92     V_SELECTEDPLAYLIST = 12,
93     // Change notifications
94     V_MIX_CHANGED = 21,
95     V_PLAYLISTS_CHANGED = 22,
96     V_PLAYLISTRATING_CHANGED = 23, // Also actions changed
97     V_SOURCES_CHANGED = 24,
98     V_SONGRATING_CHANGED = 25,
99     V_QUEUE_CHANGED = 26,
100     V_YELL = 31, // Misc
101     V_USERACTION = 32,
102     V_SERVER_STATUS = 33,
103     V_SOURCE_STATUS = 34,
104     // Queue randomization/Selection method stuff
105     V_SELECTIONMETHOD = 41,
106 
107     // Status messages
108 
109     // Data fields, may occur spontaneously or in a data response
110     I_WELCOME = 100,
111     I_ID = 111, /* Song ID */ // 111-129 Playlist/artist/track field ids
112     I_ALBUM = 112,
113     I_ARTIST = 113,
114     I_SONG = 114,
115     I_PLAYLIST = 115,
116     I_RATING = 116,
117     I_INFO_URL = 117,
118     I_COVERART = 118,
119     I_GENRE = 119,
120     I_PLAYLISTRATING = 120,
121     I_CHOICEEXPLANATION = 121,
122     I_OWNER = 122,
123     I_SOURCE = 123,
124     I_NAME = 124,
125     I_YEAR = 125,
126     I_DURATION = 126,
127     I_ACTIONS = 127,
128     I_INFO = 132,
129     I_USER_PRIVILEGES = 136,
130     // pianod settings
131     I_VOLUME = 141,
132     I_HISTORYSIZE = 142,
133     I_AUDIOQUALITY = 143,
134     I_AUTOTUNE_MODE = 144,
135     I_PAUSE_TIMEOUT = 146,
136     I_PLAYLIST_TIMEOUT = 147,
137     I_ROOM = 148,
138     // Pandora communication settings
139     I_PROXY = 161,
140     I_CONTROLPROXY = 162,
141     // 163..169 available for reuse mid 2021.
142     I_PANDORA_USER = 170,
143     I_PANDORA_PASSWORD = 171,
144     I_CACHE_MINIMUM = 172,
145     I_CACHE_MAXIMUM = 173,
146     I_OUTPUT_DRIVER = 181,
147     I_OUTPUT_DEVICE = 182,
148     I_OUTPUT_ID = 183,
149     I_OUTPUT_SERVER = 184,
150 
151     // Success/status messages, exactly one occurs (except for lists, as noted below)
152     // in response to commands.
153     S_OK = 200,
154     S_ANSWER_YES = 201,
155     S_ANSWER_NO = 202,
156     S_DATA = 203, // For multi line items: Occurs 0 or more times, once before each listed group.
157     // For single line items: occurs once
158     S_DATA_END = 204, // Occurs exactly once after last list item
159     S_SIGNOFF = 205,
160     S_MATCH = 206, ///< Matches for criteria were found
161     S_ROUNDING = 207, ///< Success but with rounding
162     S_NOOP = 208, ///< Nothing to do; default success.
163     S_PARTIAL = 209, ///< Command partially succeeded, but there were failures.
164     S_PENDING = 210, ///< Request is pending but will succeed eventually
165 
166     // Diagnostic messages, always followed by an E_* 400-499 message.
167     // Values parallel E_* messages, but do not conclude response.
168     // May occur multiple times per command.
169     D_DIAGNOSTICS = 300,
170     D_NOTFOUND = 304,
171     D_DIAGNOSTICS_END = 399,
172 
173     /*  Errors for user failures, unauthorized actions, bad commands,
174         the connection is down, etc.  Always in response to a command,
175         one per command. */
176     E_BAD_COMMAND = 400,
177     E_UNAUTHORIZED = 401,
178     E_NAK = 402, // negative acknowledgement
179     E_DUPLICATE = 403,
180     E_NOTFOUND = 404,
181     E_WRONG_STATE = 405,
182     E_CREDENTIALS = 406,
183     E_INVALID = 407,
184     E_TRANSFORM_FAILED = 408,
185     E_CONFLICT = 409,
186     E_REQUESTPENDING = 410, ///< Request couldn't be completed now, we'll try again later.
187     E_QUOTA = 411, ///< Quota restriction encountered.
188     E_LOGINREQUIRED = 412, ///< Command/feature requires user be logged in
189     E_UNSUPPORTED = 413, // Not allowed by media source/media player
190     E_RESOURCE = 414, ///< Inadequate memory, disk, etc.
191     E_RANGE = 415, ///< Request puts value out of range
192     E_METAPLAYLIST = 416, ///< Require a real playlist
193     E_WRONGTYPE = 417, ///< Operand was of wrong type
194     E_PERSISTENT = 418, ///< Require persistent data
195     E_AMBIGUOUS = 419, ///< Ambiguous expression
196     E_TYPE_DISALLOWED = 420, ///< Type not allowed with expression
197     E_PARTIAL = 421, ///< Partial failure, but a portion succeeded.
198     E_VARIOUS = 422, ///< All failures, but a varity of reasons.
199     E_NO_ASSOCIATION = 423, ///< Missing association data, i.e., song has no playlist.
200     E_EXPRESSION = 424, ///< Invalid expression
201     E_TIMEOUT = 425, ///< Event did not occur within specified duration
202     E_PLAYLIST_REQUIRED = 426, ///< Song must have a playlist for this action.
203     /* Server failures, like out of memory, etc.
204      Not a response to a particular command, although a command may initiate what causes them */
205     E_MEDIA_ACTION = 460, ///< Source doesn't do that
206     E_MEDIA_VALUE = 461, ///< Source doesn't accept a certain value
207     E_MEDIA_MANAGER = 462, ///< Can't do that on media manager
208     E_MEDIA_FAILURE = 463, ///< Source failure
209     E_MEDIA_TRANSIENT = 464, ///< Transient playlist
210     E_BUG = 498, ///< A bug was encountered (likely hit default in a case statement.)
211     E_NOT_IMPLEMENTED = 499,
212 
213     // Failures: Spontaneous problems, unrelated to a command.
214     F_FAILURE = 500,
215     F_PLAYER_EMPTY = 501,
216     F_NETWORK_FAILURE = 502,
217     F_SHUTDOWN = 503,
218     F_AUTHENTICATION = 504,
219     F_RESOURCE = 505,
220     F_PANDORA = 507,
221     F_INCOMPLETE = 508,
222     F_PERMISSION = 509,
223     F_EXCEPTION = 510,
224     F_NETWORK_TIMEOUT = 511,
225     F_CANNOT_OUTPUT = 512,
226     F_AUDIO_FAILURE = 513,
227 
228     // Action messages.  These are used to lookup text, in case we want to internationalize messaging.
229     A_SIGNED_IN = 1000,
230     A_SIGNED_OUT = 1001,
231     A_KICKED = 1002,
232     A_IMBECILE =1003,
233     A_SKIPPED = 1010,
234     A_STOPPED = 1011,
235     A_PAUSED = 1012,
236     A_RESUMED = 1013,
237     A_CHANGED_MIX = 1014,
238     A_MIX_ADDED = 1015,
239     A_MIX_REMOVED = 1016,
240     A_REQUESTS = 1017,
241     A_RANDOMPLAY = 1018,
242     A_SELECTED_PLAYLIST = 1020,
243     A_CREATED_PLAYLIST = 1021,
244     A_RENAMED_PLAYLIST = 1022,
245     A_DELETED_PLAYLIST = 1023,
246     A_SOURCE_ADD = 1030,
247     A_SOURCE_BORROW = 1031,
248     A_SOURCE_REMOVE = 1032,
249     A_REQUEST_ADD = 1040,
250     A_REQUEST_CLEAR = 1041,
251     A_REQUEST_CANCEL = 1042,
252     A_SHUTDOWN = 1100
253 } RESPONSE_CODE;
254 
255 /** Exception for command execution problems.  If not caught, the connection's
256     exception handler sends out the contained response. */
257 class CommandError final : public std::exception {
258 private:
259     const RESPONSE_CODE _reason;
260     char *detail = nullptr;
261 public:
CommandError(RESPONSE_CODE r)262     CommandError (RESPONSE_CODE r) : _reason (r) { };
CommandError(RESPONSE_CODE r,const char * message)263     CommandError (RESPONSE_CODE r, const char *message)
264     : _reason (r), detail (strdup (message)) { };
CommandError(RESPONSE_CODE r,const std::string & message)265     CommandError (RESPONSE_CODE r, const std::string &message)
266     : CommandError (r, message.c_str()) {};
267     CommandError (const CommandError &) = delete;
CommandError(CommandError && from)268     CommandError (CommandError &&from)
269     : _reason (from._reason), detail (from.detail) {
270         from.detail = nullptr;
271     }
272     CommandError &operator = (const CommandError &) = delete;
273     CommandError &operator = (CommandError &&from) {
274         *const_cast<RESPONSE_CODE *> (&_reason) = from._reason;
275         detail = from.detail;
276         from.detail = nullptr;
277         return *this;
278     }
reason(void)279     inline RESPONSE_CODE reason (void) const { return _reason; };
what()280     virtual inline const char *what() const throw() override {
281         return detail ? detail : "";
282     };
283 };
284 
285 class InitializationException : public std::exception {
286     std::string _detail;
287     const char *detail = nullptr;
288 public:
InitializationException()289     InitializationException () { };
InitializationException(const char * reason)290     InitializationException (const char *reason) : detail (reason) { };
InitializationException(const char * library,const char * reason)291     InitializationException (const char *library, const char *reason) :
292     _detail (std::string (library) + ": " + reason) { detail = _detail.c_str(); };
what()293     virtual inline const char *what() const throw() override {
294         return detail ? detail : "No explanation";
295     };
296 };
297 
isSuccess(RESPONSE_CODE code)298 inline bool isSuccess (RESPONSE_CODE code) {
299     return code >= 200 && code < 300;
300 }
301 
302 #define CMD_RANGE_SERVICE (1000)
303 #define CMD_RANGE_ENGINE (2000)
304 #define CMD_RANGE_USER (3000)
305 #define CMD_RANGE_MEDIA_MANAGER (4000)
306 #define CMD_RANGE_TUNING (5000)
307 #define CMD_RANGE_PANDORA (10000)
308 #define CMD_RANGE_TONEGENERATOR (11000)
309 #define CMD_RANGE_FILESYSTEM (12000)
310 #define CMD_RANGE_SPOTIFY (13000)
311 
312 const int CMD_INVALID = 0;
313 
314 
315 
316 class PianodService;
317 class User;
318 
319 typedef std::vector<const User *> UserList;
320 
321 /** Privilege management for media sources. */
322 class Ownership {
323 public:
324     /// Access levels for an object.
325     /// @warn Enumeration values and order matter (code compares access & actions).
326     enum class Type {
327         DISOWNED,       ///< Object has no owner
328         PRIVATE,        ///< Visible by owner only
329         SHARED,         ///< Others can use this, but not derive from it
330         PUBLISHED,      ///< Use and derive by anyone but only owner can manipulate
331         PUBLIC          ///< Anyone can manipulate this
332     };
333     /// Access actions for an object.
334     /// @warn Enumeration values and order matter (code compares access & actions).
335     enum class Action {
336         SEE,            ///< View existence of item
337         USE,            ///< Use the item to do something
338         READ,           ///< Read the item's contents/configuration
339         ALTER           ///< Change the item
340     };
341 
342     virtual bool isOwnedBy (const User *user) const = 0;
343     virtual bool hasPermission (const User *user, Action action) const = 0;
344 
isVisibleBy(const User * user)345     inline bool isVisibleBy (const User *user) const {
346         return hasPermission (user, Action::SEE);
347     }
isUsableBy(const User * user)348     inline bool isUsableBy (const User *user) const {
349         return hasPermission (user, Action::USE);
350     }
isDecendableBy(const User * user)351     inline bool isDecendableBy (const User *user) const  {
352         return hasPermission (user, Action::READ);
353     }
isReadableBy(const User * user)354     inline bool isReadableBy (const User *user) const  {
355         return hasPermission (user, Action::READ);
356     }
isEditableBy(const User * user)357     inline bool isEditableBy (const User *user) const  {
358         return hasPermission (user, Action::ALTER);
359     }
360 };
361 
362 #endif
363