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