diff --git a/Doxyfile b/Doxyfile index c22c59df..0fb84b18 100644 --- a/Doxyfile +++ b/Doxyfile @@ -672,7 +672,7 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = DoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib diff --git a/DoxygenLayout.xml b/DoxygenLayout.xml new file mode 100644 index 00000000..de524405 --- /dev/null +++ b/DoxygenLayout.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index d36bf76b..f0b0fb88 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -1,6 +1,31 @@ +/// \page api API calls +/// The controller listens for commands through a JSON-based API. This page describes the API in full. +/// +/// A default interface implementing this API as a single HTML page is included in the controller itself. This default interface will be send for invalid API requests, and is thus triggered by default when a browser attempts to access the API port directly. +/// The default API port is 4242 - but this can be changed through both the API and commandline parameters. +/// +/// To send an API request, simply send a HTTP request to this port for any file, and include either a GET or POST parameter called `"command"`, containing a JSON object as payload. Nearly all members of the request object are optional, and described below. +/// A simple example request logging in to the system would look like this: +/// +/// GET /api?command={"authorize":{"username":"test","password":"941d7b88b2312d4373aff526cf7b6114"}} HTTP/1.0 +/// +/// Or, when properly URL encoded: +/// +/// GET /api?command=%7B%22authorize%22%3A%7B%22username%22%3A%22test%22%2C%22password%22%3A%22941d7b88b2312d4373aff526cf7b6114%22%7D%7D HTTP/1.0 +/// +/// The server is quite lenient about not URL encoding your strings, but it's a good idea to always do it, anyway. +/// See the `"authorize"` section below for more information about security and logging in. +/// +/// As mentioned above, sending an invalid request will trigger a response containing the default interface. As you may not want to receive a big HTML page as response to an invalid request, requesting the file `"/api"` (as done in the example above) will force a JSON response, even when the request is invalid. +/// +/// You may also include a `"callback"` or `"jsonp"` HTTP variable, to trigger JSONP compatibility mode. JSONP is useful for getting around the cross-domain scripting protection in most modern browsers. Developers creating non-JavaScript applications will most likely not want to use JSONP mode, though nothing is stopping you if you really want to. +/// +/// \brief Listing of all controller API calls. + /// \file controller.cpp /// Contains all code for the controller executable. + #include //#include #include @@ -66,6 +91,45 @@ namespace Controller { ///\param Request The request to be parsed. ///\param Response The location to store the generated response. ///\param conn The user to be checked for authorization. + /// + /// \api + /// To login, an `"authorize"` request must be sent. Since HTTP does not use persistent connections, you are required to re-sent authentication with every API request made. To prevent plaintext sending of the password, a random challenge string is sent first, and then the password is hashed together with this challenge string to create a one-time-use string to login with. + /// If the user is not authorized, this request is the only request the server will respond to until properly authorized. + /// `"authorize"` requests take the form of: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// //username to login as + /// "username": "test", + /// //hash of password to login with. Send empty value when no challenge for the hash is known yet. + /// //When the challenge is known, the value to be used here can be calculated as follows: + /// // MD5( MD5("secret") + challenge) + /// //Where "secret" is the plaintext password. + /// "password": "" + /// } + /// ~~~~~~~~~~~~~~~ + /// and are responded to as: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// //current login status. Either "OK", "CHALL", "NOACC" or "ACC_MADE". + /// "status": "CHALL", + /// //Random value to be used in hashing the password. + /// "challenge": "abcdef1234567890" + /// } + /// ~~~~~~~~~~~~~~~ + /// The challenge string is sent for all statuses, except `"NOACC"`, where it is left out. + /// A status of `"OK"` means you are currently logged in and have access to all other API requests. + /// A status of `"CHALL"` means you are not logged in, and a challenge has been provided to login with. + /// A status of `"NOACC"` means there are no valid accounts to login with. In this case - and ONLY in this case - it is possible to create a initial login through the API itself. To do so, send a request as follows: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// //username to create, as plain text + /// "new_username": "test", + /// //password to set, as plain text + /// "new_password": "secret" + /// } + /// ~~~~~~~~~~~~~~~ + /// Please note that this is NOT secure. At all. Never use this mechanism over a public network! + /// A status of `"ACC_MADE"` indicates the account was created successfully and can now be used to login as normal. void Authorize(JSON::Value & Request, JSON::Value & Response, ConnectedUser & conn){ time_t Time = time(0); tm * TimeInfo = localtime( &Time); @@ -113,6 +177,47 @@ namespace Controller { ///\brief Check the submitted configuration and handle things accordingly. ///\param in The new configuration. ///\param out The location to store the resulting configuration. + /// + /// \api + /// `"config"` requests take the form of: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// "controller": { //controller settings + /// "interface": null, //interface to listen on. Defaults to all interfaces. + /// "port": 4242, //port to listen on. Defaults to 4242. + /// "username": null //username to drop privileges to. Defaults to root. + /// }, + /// "protocols": [ //enabled connectors / protocols + /// { + /// "connector": "HTTP" //Name of the connector to enable + /// //any required and/or optional settings may be given here as "name": "value" pairs inside this object. + /// }, + /// //above structure repeated for all enabled connectors / protocols + /// ], + /// "serverid": "", //human-readable server identifier, optional. + /// } + /// ~~~~~~~~~~~~~~~ + /// and are responded to as: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// "controller": { //controller settings + /// "interface": null, //interface to listen on. Defaults to all interfaces. + /// "port": 4242, //port to listen on. Defaults to 4242. + /// "username": null //username to drop privileges to. Defaults to root. + /// }, + /// "protocols": [ //enabled connectors / protocols + /// { + /// "connector": "HTTP" //Name of the connector to enable + /// //any required and/or optional settings may be given here as "name": "value" pairs inside this object. + /// "online": 1 //boolean value indicating if the executable is running or not + /// }, + /// //above structure repeated for all enabled connectors / protocols + /// ], + /// "serverid": "", //human-readable server identifier, as configured. + /// "time": 1398982430, //current unix time + /// "version": "2.0.2/8.0.1-23-gfeb9322/Generic_64" //currently running server version string + /// } + /// ~~~~~~~~~~~~~~~ void CheckConfig(JSON::Value & in, JSON::Value & out){ for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ if (jit->first == "version" || jit->first == "time"){ @@ -536,6 +641,14 @@ int main(int argc, char ** argv){ Response["conversion"]["status"] = myConverter.getStatus(); } } + /// + /// \api + /// `"save"` requests are always empty: + /// ~~~~~~~~~~~~~~~{.js} + /// {} + /// ~~~~~~~~~~~~~~~ + /// Sending this request will cause the controller to write out its currently active configuration to the configuration file it was loaded from (the default being `./config.json`). + /// if (Request.isMember("save")){ if( Controller::WriteFile(Controller::conf.getString("configFile"), Controller::Storage.toString())){ Controller::Log("CONF", "Config written to file on request through API"); @@ -554,6 +667,21 @@ int main(int argc, char ** argv){ Response["config"]["serverid"] = ""; } //sent any available logs and statistics + /// + /// \api + /// `"log"` responses are always sent, and cannot be requested: + /// ~~~~~~~~~~~~~~~{.js} + /// [ + /// [ + /// 1398978357, //unix timestamp of this log message + /// "CONF", //shortcode indicating the type of log message + /// "Starting connector: {\"connector\":\"HTTP\"}" //string containing the log message itself + /// ], + /// //the above structure repeated for all logs + /// ] + /// ~~~~~~~~~~~~~~~ + /// It's possible to clear the stored logs by sending an empty `"clearstatlogs"` request. + /// Response["log"] = Controller::Storage["log"]; //clear log and statistics if requested if (Request.isMember("clearstatlogs")){ diff --git a/src/controller/controller_capabilities.cpp b/src/controller/controller_capabilities.cpp index 9d661666..884bf5b3 100644 --- a/src/controller/controller_capabilities.cpp +++ b/src/controller/controller_capabilities.cpp @@ -51,6 +51,74 @@ namespace Controller { ///\brief Checks the capabilities of the system. ///\param capa The location to store the capabilities. + /// + /// \api + /// `"capabilities"` requests are always empty: + /// ~~~~~~~~~~~~~~~{.js} + /// {} + /// ~~~~~~~~~~~~~~~ + /// and are responded to as: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// "connectors": { // a list of installed connectors + /// "FLV": { //name of the connector. This is based on the executable filename, with the "MistIn" / "MistConn" prefix stripped. + /// "codecs": [ //supported combinations of codecs. + /// [["H264","H263","VP6"],["AAC","MP3"]] //one such combination, listing simultaneously available channels and the codec options for those channels + /// ], + /// "deps": "HTTP", //dependencies on other connectors, if any. + /// "desc": "Enables HTTP protocol progressive streaming.", //human-friendly description of this connector + /// "methods": [ //list of supported request methods + /// { + /// "handler": "http", //what handler to use for this request method. The "http://" part of a URL, without the "://". + /// "priority": 5, // priority of this request method, higher is better. + /// "type": "flash/7" //type of request method - usually name of plugin followed by the minimal plugin version, or 'HTML5' for pluginless. + /// } + /// ], + /// "name": "HTTP_Progressive_FLV", //Full name of this connector. + /// "optional": { //optional parameters + /// "username": { //name of the parameter + /// "help": "Username to drop privileges to - default if unprovided means do not drop privileges", //human-readable help text + /// "name": "Username", //human-readable name of parameter + /// "option": "--username", //command-line option to use + /// "type": "str" //type of option - "str" or "num" + /// } + /// //above structure repeated for all (optional) parameters + /// }, + /// //above structure repeated, as "required" for required parameters, if any. + /// "socket": "http_progressive_flv", //unix socket this connector listens on, if any + /// "url_match": "/$.flv", //URL pattern to match, if any. The $ substitutes the stream name and may not be the first or last character. + /// "url_prefix": "/progressive/$/", //URL prefix to match, if any. The $ substitutes the stream name and may not be the first or last character. + /// "url_rel": "/$.flv" //relative URL where to access a stream through this connector. + /// } + /// //... above structure repeated for all installed connectors. + /// }, + /// "cpu": [ //a list of installed CPUs + /// { + /// "cores": 4, //amount of cores for this CPU + /// "mhz": 1645, //speed in MHz for this CPU + /// "model": "Intel(R) Core(TM) i7-2630QM CPU @ 2.00GHz", //model identifier, for humans + /// "threads": 8 //amount of simultaneously executing threads that are supported on this CPU + /// } + /// //above structure repeated for all installed CPUs + /// ], + /// "load": { + /// "fifteen": 72, + /// "five": 81, + /// "memory": 42, + /// "one": 124 + /// }, + /// "mem": { + /// "cached": 1989, //current memory usage of system caches, in MiB + /// "free": 2539, //free memory, in MiB + /// "swapfree": 0, //free swap space, in MiB + /// "swaptotal": 0, //total swap space, in MiB + /// "total": 7898, //total memory, in MiB + /// "used": 3370 //used memory, in MiB (excluding system caches, listed separately) + /// }, + /// "speed": 6580, //total speed in MHz of all CPUs cores summed together + /// "threads": 8 //total count of all threads of all CPUs summed together + /// } + /// ~~~~~~~~~~~~~~~ void checkCapable(JSON::Value & capa){ //capa.null(); capa.removeMember("cpu"); diff --git a/src/controller/controller_statistics.cpp b/src/controller/controller_statistics.cpp index 23848904..02a5c38a 100644 --- a/src/controller/controller_statistics.cpp +++ b/src/controller/controller_statistics.cpp @@ -91,7 +91,7 @@ bool Controller::hasViewers(std::string streamName){ /// This takes a "clients" request, and fills in the response data. /// /// \api -/// `"client"` requests take the form of: +/// `"clients"` requests take the form of: /// ~~~~~~~~~~~~~~~{.js} /// { /// //array of streamnames to accumulate. Empty means all. diff --git a/src/controller/controller_streams.cpp b/src/controller/controller_streams.cpp index d0c4c8f0..5bec9167 100644 --- a/src/controller/controller_streams.cpp +++ b/src/controller/controller_streams.cpp @@ -305,6 +305,52 @@ namespace Controller { ///\brief Parse a given stream configuration. ///\param in The requested configuration. ///\param out The new configuration after parsing. + /// + /// \api + /// `"streams"` requests take the form of: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// "streamname_here": { //name of the stream + /// "source": "/mnt/media/a.dtsc" //full path to a VoD file, or "push://" followed by the IP or hostname of the machine allowed to push live data. Empty means everyone is allowed to push live data. + /// "DVR": 30000 //optional. For live streams, indicates the requested minimum size of the available DVR buffer in milliseconds. + /// }, + /// //the above structure repeated for all configured streams + /// } + /// ~~~~~~~~~~~~~~~ + /// and are responded to as: + /// ~~~~~~~~~~~~~~~{.js} + /// { + /// "streamname_here": { //name of the configured stream + /// "error": "Available", //error state, if any. "Available" is a special value for VoD streams, indicating it has no current viewers (is not active), but is available for activation. + /// "h_meta": 1398113185, //unix time the stream header (if any) was last processed for metadata + /// "l_meta": 1398115447, //unix time the stream itself was last processed for metadata + /// "meta": { //available metadata for this stream, if any + /// "format": "dtsc", //detected media source format + /// "tracks": { //list of tracks in this stream + /// "audio_AAC_2ch_48000hz_2": {//human-readable track name + /// "bps": 16043, + /// "channels": 2, + /// "codec": "AAC", + /// "firstms": 0, + /// "init": "\u0011Vå\u0000", + /// "lastms": 596480, + /// "rate": 48000, + /// "size": 16, + /// "trackid": 2, + /// "type": "audio" + /// }, + /// //the above structure repeated for all tracks + /// }, + /// "vod": 1 //indicates VoD stream, or "live" to indicated live stream. + /// }, + /// "name": "a", //the stream name, guaranteed to be equal to the object name. + /// "online": 2, //online state. 0 = error, 1 = active, 2 = inactive. + /// "source": "/home/thulinma/a.dtsc" //source for this stream, as configured. + /// }, + /// //the above structure repeated for all configured streams + /// } + /// ~~~~~~~~~~~~~~~ + /// Through this request, ALL streams must always be configured. To remove a stream, simply leave it out of the request. To add a stream, simply add it to the request. To edit a stream, simply edit it in the request. The LTS edition has additional requests that allow per-stream changing of the configuration. void CheckStreams(JSON::Value & in, JSON::Value & out){ //check for new streams and updates AddStreams(in, out);