All pastes #176813 Raw Edit

daapd daapd.cc

public cpp v1 · immutable
#176813 ·published 2006-09-19 23:35 UTC
rendered paste body
/*	daapd 0.2.3d, a server for the DAA protocol	(c) deleet 2003, Alexander Oberdoerster	main program		daapd is free software; you can redistribute it and/or modify	it under the terms of the GNU General Public License as published by	the Free Software Foundation; either version 2 of the License, or	(at your option) any later version.		daapd is distributed in the hope that it will be useful,	but WITHOUT ANY WARRANTY; without even the implied warranty of	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	GNU General Public License for more details.		You should have received a copy of the GNU General Public License	along with daapd; if not, write to the Free Software	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*/#include "types.h"#include "dboutput.h"#include <iostream>#include <cstring>#include <cassert>#include <unistd.h>#include <errno.h>#include <sys/time.h>#include <sys/param.h>#include <math.h>#include <httpd-persistent.h>#include <zlib.h>#include <daap/tagoutput.h>#include <daap/registry.h>#include <pthread.h>#ifdef HOWL_ENABLE	#include <howl.h>#endifconst int   DAAP_OK	  = 200;const char* DAAPD_VERSION = "daapd 0.2.3d";#if( UCHAR_MAX != 0xFF )	#error "change size of 'char' to 8-bit"#endif#ifndef __BASIC_DAAP__	#error please install daaplib (http://www.deleet.de/projekte/daap/daaplib)#endifusing namespace std;// utility functionsinline ostream& operator << ( ostream& out, const std::vector<std::string> *stringvect ) {	u32 i;	for( i = 0; i < stringvect->size() - 1; ++i ) 		out << (*stringvect)[i] << ", ";	if( stringvect->size() != 0 )		out << (*stringvect)[i];	return( out );}ComponentVect *tokenizeString( ComponentVect& components, const char* s_, const char delim ) {	std::string s( s_ );	std::string::iterator component = s.begin();	// skip the first delim	if ( find( component, s.end(), delim ) == s.begin() )		component++;	std::string::iterator nextDelim;	while ( ( nextDelim = find( component, s.end(), delim ) ) != s.end() ) {		components.push_back( std::string( component, nextDelim ) );		component = nextDelim + 1;	}	if ( component != s.end() )		components.push_back( std::string( component, s.end() ) );			return &components;}// httpd helper/*#define USEC_PER_SECOND		1000000double	get_time() {	struct timeval  tv;	double res;		if (gettimeofday(&tv,NULL)) {		perror("get_time");		return -1.0;	}	res = (tv.tv_sec + ((double) tv.tv_usec) / USEC_PER_SECOND);	return res;}*/void sendTag( httpd* server, const Chunk& c, const bool zipped ) {	httpdSetContentType( server, "application/x-dmap-tagged" );	if ( strncmp( httpdRequestAcceptEncoding( server ), "gzip", 4) == 0 && 	     c.size() > 1000 && zipped ) {		// double before = get_time();		z_stream strm;		int status = 0;		int compressed = false;			strm.zalloc = Z_NULL;		strm.zfree = Z_NULL;		strm.opaque = Z_NULL;			strm.next_in = (Bytef *)c.begin();		strm.avail_in = c.size();		int zBufLen = (int) ceil(1.001 * c.size()) + 12 + 6; // gzip has 18 Bytes Header + Trailer		Bytef *zBuffer = (Bytef *)malloc( zBufLen );			strm.next_out = zBuffer;		strm.avail_out = zBufLen;			int windowBits =  15    // the default				+ 16;   // for gzip header; 					// see deflateInit2() documentation in zlib.h			// compression level: 1 = fastest, 6 = Z_DEFAULT_COMPRESSION, 9 = best		if( ( status = deflateInit2( &strm, 1, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY ) ) == Z_OK ) {			// run until the buffer is processed or there is an error			while( ( status = deflate( &strm, Z_FINISH ) ) == Z_OK );			if( status == Z_STREAM_END )				compressed = true;		}		// double after = get_time();		// cerr << "gzip took " << after - before << " seconds. " << c.size() << " Bytes compressed to " << strm.total_out << " Bytes. Compression Ratio " << (double)strm.total_out / (double)c.size() * 100.0 << "%." << endl;				if (compressed) { 			httpdAddHeader ( server, "Content-Encoding: gzip" );			httpdWrite( server, (char*) zBuffer, strm.total_out );		} else {			// perror("compress");			// cerr << "Return code is " << status << endl;			httpdWrite( server, (char*) c.begin(), c.size() );		}			free(zBuffer);	} else { 		httpdWrite( server, (char*) c.begin(), c.size() );	}}// httpd stuffvoid sendServerInfo( httpd *server, const Database& db  ) {	Version daapVersion( 3, 0 );	Version dmapVersion( 2, 0 );	double version = httpdRequestDaapVersion( server );	if( version == 1.0 ) {		daapVersion.hi = 1;		daapVersion.lo = 0;		dmapVersion.hi = 1;		dmapVersion.lo = 0;	} else if( version == 2.0 ) {		daapVersion.hi = 2;		daapVersion.lo = 0;		dmapVersion.hi = 1;		dmapVersion.lo = 0;	}		TagOutput response;	response << Tag( 'msrv' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('mpro') <<			dmapVersion <<		end <<		Tag('apro') <<			daapVersion <<		end <<		Tag('minm') <<			db.serverName <<		end <<		Tag('mslr') <<			(u8) 0 <<		end <<		Tag('msal') <<			(u8) 0 <<		end <<		Tag('mstm') <<			(u32) 1800 <<		end <<		Tag('msup') <<			(u8) 0 <<		end <<		Tag('msau') <<			(u32) 2 <<		end <<// Disable some features for now/*		Tag('mspi') <<			(u8) 0 <<		end <<		Tag('msex') <<			(u8) 0 <<		end <<		Tag('msbr') <<			(u8) 0 <<		end <<		Tag('msqy') <<			(u8) 0 <<		end <<		Tag('msix') <<			(u8) 0 <<		end <<		Tag('msrs') <<			(u8) 0 <<		end <<*/		Tag('msdc') <<			(u32) 1 <<		end <<	end;	sendTag( server, response.data(), db.compression );}void sendContentCodes( httpd *server ) {	sendTag( server, TypeRegistry::getDictionary(), false );}void sendLogin( httpd *server, u32 sessionId ) {	TagOutput response;	response << Tag( 'mlog' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('mlid') <<			(u32) sessionId <<		end <<	end;	sendTag( server, response.data(), false );}void sendUpdate( httpd *server, u32 revision ) {	cerr << "sendUpdates" << endl;	TagOutput response;	response << Tag( 'mupd' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('musr') <<			revision <<		end <<	end;	sendTag( server, response.data(), false );}void distributeUpdates( httpd *server, u32 revision ) {	cerr << "distributeUpdates" << endl;	TagOutput response;	response << Tag( 'mupd' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('musr') <<			revision <<		end <<	end;	httpdSetContentType( server, "application/x-dmap-tagged" );	httpdWriteSubscribers( server, (char*) response.data().begin(), response.data().size() );}void sendDatabaseList( httpd *server, const Database& db ) {	TagOutput response;	response << Tag( 'avdb' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('muty') <<			(u8) 0 <<		end <<		Tag('mtco') <<			(u32) 1 <<		end <<		Tag('mrco') <<			(u32) 1 <<		end <<		Tag('mlcl') <<			Tag('mlit') <<				Tag('miid') <<					(u32) 1 <<				end <<//				Tag('mper') <<//					(u64) 543675286654 <<//				end <<				Tag('minm') <<					db.dbName <<				end <<				Tag('mimc') <<					(u32) db.songs.size() <<				end <<				Tag('mctc') <<					(u32) db.containers.size() <<				end <<			end <<		end <<	end;	sendTag( server, response.data(), db.compression );}int sendDatabase( httpd *server, const Database& db, const u64& meta ) { 	TagOutput response;	response << Tag( 'adbs' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('muty') <<			(u8) 0 <<		end <<		Tag('mtco') <<			(u32) db.songs.size() <<		end <<		Tag('mrco') <<			(u32) db.songs.size() <<		end <<		Tag('mlcl') <<			Filtered( db.songs, meta ) <<		end <<	end;	sendTag( server, response.data(), db.compression );		return 0;}int sendDatabaseItem( httpd *server, const Song& song ) {	httpdSendFile( server, (char *) song.path.c_str(), httpdRequestContentRange( server ) );		return 0;}void sendContainers( httpd *server, const Database& db, const u64& meta ) {	printf ("Containers : %d\n\n",db.containers.size());	TagOutput response;	response << Tag( 'aply' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('muty') <<			(u8) 0 <<		end <<		Tag('mtco') <<			(u32) db.containers.size() <<		end <<		Tag('mrco') <<			(u32) db.containers.size() <<		end <<		Tag('mlcl') <<			db.containers <<		end <<	end;	sendTag( server, response.data(), db.compression );}int sendContainer( httpd *server, const Container& container, const u64& meta, const bool compressed ) {	TagOutput response;	response << Tag( 'apso' ) <<		Tag('mstt') <<			(u32) DAAP_OK <<		end <<		Tag('muty') <<			(u8) 0 <<		end <<		Tag('mtco') <<			(u32) container.size() <<		end <<		Tag('mrco') <<			(u32) container.size() <<		end <<		Tag('mlcl') <<			Filtered( container.items, meta) <<		end <<	end;	sendTag( server, response.data(), compressed );	return 0;}void sendAdminRoot( httpd *server, const Database& db ) {	std::string adminRoot = "<html><head><title>daapd</title></head><body>This will turn into a full-blown admin interface someday. Hopefully.</body></html>";	httpdWrite( server, (char*) adminRoot.c_str(), adminRoot.size() );}void sendRescanConfirmation( httpd *server ) {	std::string confirmation = "<html><head><title>daapd server</title></head><body>Rescan finished.</body></html>";	httpdWrite( server, (char*) confirmation.c_str(), confirmation.size() );}// parsing and decision treeint parseDatabases( httpd *server, Database& db, char *path, int delta, const u64& meta ) {	// (just ignore delta for now)	ComponentVect components;	components = *tokenizeString( components, path, '/' );	assert( components.size() > 0 );	if ( components.size() == 1 ) {		assert( components[0] == "databases" );		sendDatabaseList( server, db );	} else {		u32 dbId = strtol( components[1].c_str(), NULL, 10 );		// just a single database for now		if ( db.id != dbId || components.size() < 3) {			httpdSend403( server );			return -1;		} else {			if( components[2] == "items" ) {				if( components.size() == 3 ) {					sendDatabase( server, db, meta );				} else if ( components.size() == 4 ) {					u32 songId = strtol( components[3].c_str(), NULL, 10 );					Song *song; 					pthread_mutex_lock( &(db.dbLock) );					if( ( song = db.songs.findId( songId ) ) == NULL ) {						httpdSend403( server );					    pthread_mutex_unlock( &(db.dbLock) );						return -1;					} else {						sendDatabaseItem( server, *song );					}					pthread_mutex_unlock( &(db.dbLock) );				} else {					httpdSend403( server );					return -1;				}			} else if( components[2] == "containers" ) {				if( components.size() == 3 ) {					sendContainers( server, db, meta );				} else if ( components.size() == 5 && components[4] == "items" ) {					u32 contId = strtol( components[3].c_str(), NULL, 10);					Container *container; 					if( ( container = db.containers.findId( contId ) ) == NULL ) {						httpdSend403( server );						return -1;					} else {						sendContainer( server, *container, meta, db.compression );					}				} else {					httpdSend403( server );					return -1;				}			} else {				httpdSend403( server );				return -1;			}		}	}			return 0;}int parseAdmin( httpd *server, void *db_, char *path ) {	Database *db = (Database*) db_;	ComponentVect components;	components = *tokenizeString( components, path, '/' );	assert( components.size() > 0 );	assert( components[0] == "admin" );	if ( components.size() == 1 ) {		sendAdminRoot( server, *db );		return 0;	} else {		if ( components.size() == 2) {			/*			if( components[1] == "rescan" ) {				db->scan();				sendRescanConfirmation( server );				return 0;			} else {				*/				httpdSend403( server );				return -1;			// }		} else {			httpdSend403( server );			return -1;		}	}	return 0;}int parsePath( httpd *server, Request &request ) {	char *path = httpdRequestPath( server );	// first resolve the path to a request number	if ( strncmp( path, "/server-info", sizeof("/server-info") - 1 ) == 0 ) {		request.number = DAAP_SERVINFO;	} else if ( strncmp( path, "/content-codes", sizeof("/content-codes") - 1 ) == 0 ) {		request.number = DAAP_CONTCODES;	} else if ( strncmp( path, "/login", sizeof("/login") - 1 ) == 0 ) {		request.number = DAAP_LOGIN;	} else if ( strncmp( path, "/logout", sizeof("/logout") - 1 ) == 0 ) {		request.number = DAAP_LOGOUT;	} else if ( strncmp( path, "/update", sizeof("/update") - 1 ) == 0 ) {		request.number = DAAP_UPDATE;	} else if ( strncmp( path, "/databases", sizeof("/databases") - 1 ) == 0 ) {		request.number = DAAP_DATABASES;// no resolve or browse yet//	} else if ( strncmp( path, "/resolve", sizeof("/resolve") - 1 ) == 0 ) {//		request.number = DAAP_RESOLVE;//	} else if ( strncmp( path, "/browse", sizeof("/browse") - 1 ) == 0 ) {//		request.number = DAAP_BROWSE;	// Administration: Not part of DAAP	} else if ( strncmp( path, "/admin", sizeof("/admin") - 1 ) == 0 ) {		request.number = DAAP_ADMIN;	} else {		httpdSend400( server );		return -1;	}		return 0;}u64 *parseMeta( u64& meta, const char *s ) {	ComponentVect components;	components = *tokenizeString( components, s, ',' );	for( u32 i = 0; i < components.size(); ++i ) {		if( components[i] == "dmap.itemid" ) 			meta |= ITEMID;		else if( components[i] == "dmap.itemname" ) 			meta |= ITEMNAME;		else if( components[i] == "dmap.itemkind" ) 			meta |= ITEMKIND;		else if( components[i] == "dmap.persistentid" ) 			meta |= PERSISTENTID;		else if( components[i] == "daap.songalbum" ) 			meta |= SONGALBUM;		else if( components[i] == "daap.songartist" ) 			meta |= SONGARTIST;		else if( components[i] == "daap.songbitrate" ) 			meta |= SONGBITRATE;		else if( components[i] == "daap.songbeatsperminute" ) 			meta |= SONGBEATSPERMINUTE;		else if( components[i] == "daap.songcomment" ) 			meta |= SONGCOMMENT;		else if( components[i] == "daap.songcompilation" ) 			meta |= SONGCOMPILATION;		else if( components[i] == "daap.songcomposer" ) 			meta |= SONGCOMPOSER;		else if( components[i] == "daap.songdateadded" ) 			meta |= SONGDATEADDED;		else if( components[i] == "daap.songdatemodified" ) 			meta |= SONGDATEMODIFIED;		else if( components[i] == "daap.songdisccount" ) 			meta |= SONGDISCCOUNT;		else if( components[i] == "daap.songdiscnumber" ) 			meta |= SONGDISCNUMBER;		else if( components[i] == "daap.songdisabled" ) 			meta |= SONGDISABLED;		else if( components[i] == "daap.songeqpreset" ) 			meta |= SONGEQPRESET;		else if( components[i] == "daap.songformat" ) 			meta |= SONGFORMAT;		else if( components[i] == "daap.songgenre" ) 			meta |= SONGGENRE;		else if( components[i] == "daap.songdescription" ) 			meta |= SONGDESCRIPTION;		else if( components[i] == "daap.songrelativevolume" ) 			meta |= SONGRELATIVEVOLUME;		else if( components[i] == "daap.songsamplerate" ) 			meta |= SONGSAMPLERATE;		else if( components[i] == "daap.songsize" ) 			meta |= SONGSIZE;		else if( components[i] == "daap.songstarttime" ) 			meta |= SONGSTARTTIME;		else if( components[i] == "daap.songstoptime" ) 			meta |= SONGSTOPTIME;		else if( components[i] == "daap.songtime" ) 			meta |= SONGTIME;		else if( components[i] == "daap.songtrackcount" ) 			meta |= SONGTRACKCOUNT;		else if( components[i] == "daap.songtracknumber" ) 			meta |= SONGTRACKNUMBER;		else if( components[i] == "daap.songuserrating" ) 			meta |= SONGUSERRATING;		else if( components[i] == "daap.songyear" ) 			meta |= SONGYEAR;		else if( components[i] == "daap.songdatakind" ) 			meta |= SONGDATAKIND;		else if( components[i] == "daap.songdataurl" ) 			meta |= SONGDATAURL;		else if( components[i] == "com.apple.itunes.norm-volume" ) 			meta |= NORMVOLUME;		else if( components[i] == "com.apple.itunes.smart-playlist" ) 			meta |= SMARTPLAYLIST;		else if( components[i] == "dmap.containeritemid" ) 			meta |= CONTAINERITEMID;	}	return &meta;}void parseVariables( httpd *server, Request &request ) {	httpVar *varPtr;	if ( ( varPtr = httpdGetVariableByName ( server, "session-id" ) ) != NULL)		request.session = strtol( varPtr->value, NULL, 10 );	if ( ( varPtr = httpdGetVariableByName ( server, "revision-number" ) ) != NULL)		request.revision = strtol( varPtr->value, NULL, 10 );	if ( ( varPtr = httpdGetVariableByName ( server, "delta" ) ) != NULL)		request.delta = strtol( varPtr->value, NULL, 10 );	if ( ( varPtr = httpdGetVariableByName ( server, "type" ) ) != NULL)		request.type = varPtr->value;	if ( ( varPtr = httpdGetVariableByName ( server, "meta" ) ) != NULL)		request.meta = *parseMeta( request.meta, varPtr->value );	if ( ( varPtr = httpdGetVariableByName ( server, "filter" ) ) != NULL)		request.filter = varPtr->value;	if ( ( varPtr = httpdGetVariableByName ( server, "query" ) ) != NULL)		request.query = varPtr->value;	if ( ( varPtr = httpdGetVariableByName ( server, "index" ) ) != NULL)		request.index = varPtr->value;}void parseRequest( httpd *server, void *db_ ) {/*	// Works without authentication, ignores session, revision and delta	server-info	// Needs authentication, ignores session, revision and delta	content-codes, login		// Needs authentication, honors session, revision and delta	update, databases		// Needs authentication, honors session, ignores revision and delta	logout*/	Database *db = (Database*) db_;	Request request;	static Sessions sessions;	if ( parsePath( server, request ) < 0 ) {		return;	}	// check if we need authentication before proceeding	if ( request.number == DAAP_SERVINFO ) {		sendServerInfo( server, *db );	} else if ( request.number == DAAP_DATABASES ) {		// give database information and files without authentication		// because iTunes doesn't send it at this point (stupid, but true) 		parseVariables( server, request );		if( sessions.isValid( request.session ) ) {			if ( request.revision != 0 && 				request.revision != db->revision ) {				cerr << "request.revision is " << request.revision << ", db->revision is " << db->revision << endl;				sendUpdate( server, db->revision );			} else {				if ( request.delta != 0 && 					request.delta <= db->revision  ) {					parseDatabases( server, *db, httpdRequestPath( server ), request.delta, request.meta );				} else {					parseDatabases( server, *db, httpdRequestPath( server ), 0, request.meta ); // everything				}			}		}	} else {		// httpdAuthenticate just looks if there's an auth user/pwd,		// sends a 401 if there is none and returns the auto length.		if ( db->authPassword != "" )			if ( !httpdAuthenticate( server, "daap" ) || 			     db->authPassword != server->request.authPassword   ) 				return;		// client authenticated; get variables		parseVariables( server, request );		if ( request.number == DAAP_ADMIN ) {			parseAdmin( server, db, httpdRequestPath( server ) );		} else if ( request.number == DAAP_CONTCODES ) {			sendContentCodes( server );		} else if ( request.number == DAAP_LOGIN ) {			// random sessions ids			u32 sessionId = random();			sessions << sessionId;			sendLogin( server, sessionId );		} else if ( request.number == DAAP_LOGOUT ) {			if ( sessions.isValid( request.session ) ) {				httpdSend204( server );				sessions.erase( request.session );			} else {				httpdSend403( server );				return;			}		} else if ( sessions.isValid( request.session ) ) {			if ( request.revision != 0 && 			     request.revision != db->revision ) {				cerr << "B: request.revision is " << request.revision << ", db->revision is " << db->revision << endl;				sendUpdate( server, db->revision );			} else {				if ( request.number == DAAP_UPDATE ) 					httpdSubscribe( server, server->clientSock );			}		} else {			httpdSend403( server );			return;		}	}		return;}void idleFunction( httpd *server, void *db_ ) {	Database *db = (Database*) db_;	if( db->revision > db->lastRevision ) {		distributeUpdates( server, db->revision );		db->lastRevision = db->revision;	}}////////////// commandline and config file parsinginline void usage( const char *progname ) {	cout << DAAPD_VERSION << endl;	cout << "usage: " << progname << " [-dhqvz] [-c config-file] [-C cache-file] [-n name] [-p port] [-s interval] [-t vbr-limit] [-x XML file] [file/directory]...\n";}void help( const char *progname ) {	usage(progname);	cout << " -c config-file   use config-file" << endl;	cout << " -C cache-file    save to and recover from" << endl;	cout << " -d               Daemon mode - implies quiet" << endl;	cout << " -h               give this help" << endl;	cout << " -n name          advertise name" << endl;	cout << " -p port          listen on port" << endl;	cout << " -q               quiet - no output" << endl;	cout << " -s interval      rescan filesystem every <interval> seconds" << endl;	cout << " -t vbr-limit     scan mp3s for length: -1 not at all, 0 entirely, n>0 first n frames" << endl;	cout << " -v               output verbose debugging info" << endl;	cout << " -z               disable gzip compression" << endl;	cout << " -x XML-file      parse XML plist file insted of a dir scan" << endl;	cout << " -a 		   Share all playlists regardless of the shared flag"<<endl;}InitParams *parseConfig( FILE* f, InitParams& initParams ) {	char key_[32];	char val[1024];	std::string key;	while( fscanf( f, "%31s%*[ \t]%1023[^\n]", key_, val ) != EOF ) {		key = key_; 		if( key == "Password" ) {			if( val != NULL )				initParams.password = val;		} else if( key == "ServerName" ) {			if( val != NULL )				initParams.serverName = val;		} else if( key == "DBName" ) {			if( val != NULL )				initParams.dbName = val;		} else if( key == "Port" ) {			if( val != NULL )				initParams.port = strtol( val, NULL, 10 );		} else if( key == "Cache" ) {			if( val != NULL )				initParams.cacheFile = val;		} else if( key == "Timescan" ) {			if( val != NULL )				initParams.timeScan = strtol( val, NULL, 10 );		} else if( key == "RescanInterval" ) {			if( val != NULL )				initParams.rescanInterval = strtol( val, NULL, 10 );		} else if( key == "Compression" ) {			if( val != NULL ) {				if ( strtol( val, NULL, 10 ) == 0 ) {					initParams.compression = false;				}			}		}  else if( key == "Root" ) {			if( val != NULL ) {				std::string dir( val );				initParams.dirs->push_back( dir );			}		} else if( key == "XML" ) {			if( val != NULL ) {				if(strtol(val,NULL,10)!=0)					initParams.xmlparse=true;			}		} else if( key == "XMLFile" ) {			if( val != NULL ) {				initParams.xmlfile=val;			}		}		bzero( val, 1024 );	}		fclose( f );	return( &initParams );}InitParams *readConfig( InitParams& initParams ) {	FILE *conf;	if( initParams.configFile != "" ) {		conf = fopen( initParams.configFile.c_str(), "r" );				if( conf == NULL ) {			cout << "Could not open config file " << initParams.configFile;			cout << ". Trying default config file." << endl;		} else {			return( parseConfig( conf, initParams ) );		}	}		conf = fopen( "/etc/daapd.conf", "r" );	if( conf != NULL ) {		return( parseConfig( conf, initParams ) );	}	return( &initParams );}InitParams *parseArgs( int argc, char *argv[], InitParams& initParams ) {	int oc;	int i;	#ifdef __sgi__		getoptreset();	#elif __linux__		putenv( "POSIXLY_CORRECT=1" );	#elif __sun__		// do nothing	#else 		optreset = 1;	#endif	optind = 1;#define OPTS "adhqvzc:C:n:p:s:t:x:"	while ( ( oc = getopt( argc, argv, OPTS ) ) != -1 ) {		switch(oc) {		case 'c':			break;		case 'C':			initParams.cacheFile = optarg;			break;		case 'd':			initParams.quiet = true;			initParams.daemon = true;			break;		case 'n':			initParams.serverName = optarg;			initParams.dbName = initParams.serverName;			break;		case 'p':			if( ( i = strtol( optarg, NULL, 10 ) ) == 0 ) {				cout << "invalid port. using the default " << initParams.port << " instead." << endl;			} else {				initParams.port = i;			}			break;		case 'q':			initParams.quiet = true;			break;		case 's':			i = strtol( optarg, NULL, 10 );			if( errno != EINVAL )				initParams.rescanInterval = i;				break;		case 't':			i = strtol( optarg, NULL, 10 );			if( errno != EINVAL )				initParams.timeScan = i;			break;		case 'v':			initParams.verbose = true;			printf("Verbose output requested.\n");			break;		case 'z':			initParams.compression = false;			break;		case 'x':			initParams.xmlparse = true;			initParams.xmlfile = optarg;			break;		case 'a':			initParams.shareall = true;			break;		default:			usage( argv[0] );			exit(1);		}	}	while (optind < argc) {		std::string dir( argv[optind] );		initParams.dirs->push_back( dir );		++optind;	}	return &initParams;}InitParams *getConfigFile( int argc, char *argv[], InitParams& initParams ) {	int oc;	while ( ( oc = getopt( argc, argv, OPTS ) ) != -1 ) {		switch(oc) {		case 'h':			help( argv[0] );			exit(0);		case 'c':			initParams.configFile = optarg;			break;		}	}	return &initParams;}void startAsDaemon( u8 verbose ) {	if (verbose)		cout << "Forking into daemon mode..." << endl;#if defined(__sgi__)	_daemonize( 0, -1, -1, -1 );	/* chroot and close all files */#elif defined(__BSD__)	daemon( 0, 0 );			/* chroot and close stdin,out and err */#elif defined(__linux__)	daemon( 0, 0 );			/* chroot and close stdin,out and err */#elif defined(__APPLE__)	daemon( 0, 0 );			/* chroot and close stdin,out and err */#else	switch(fork()) {	case 0:	/* we are the child.. continue; */		break;	case -1: /* bang ! */		perror( "Failed to fork off as a daemon" );		exit(1);		break;	default:		exit(0);	}	setsid();	chdir("/");#ifdef _PATH_DEVNULL	{	int fd = _open(_PATH_DEVNULL, O_RDWR, 0);	if (fd != -1) {		(void)dup2(fd, STDIN_FILENO);		(void)dup2(fd, STDOUT_FILENO);		(void)dup2(fd, STDERR_FILENO);		if (fd > 2)			(void)close(fd);		}	}#endif#endif}////////////// start database scan thread// intended to be called by pthread_createvoid *scanThread( void *ptr ) {	Database *db = (Database *)ptr;	while( true ) {		sleep( db->rescanInterval );	//	struct stat file_stat;	//	stat(db->xmlfile.c_str(), &file_stat);	//	if( (u32)file_stat.st_mtime > db->lastModTime ){			if( db->verbose )				printf( "rescanning...\n" );			//db->lastModTime = file_stat.st_mtime;			db->scan();	}}void startScanThread ( Database *db, const bool verbose ) {	pthread_t tid;			/* the thread identifier */	pthread_attr_t attr; 		/* set of attributes for the thread */	/* get the default attributes */	pthread_attr_init( &attr );#if defined( _POSIX_THREAD_PRIORITY_SCHEDULING) 	/* set the scheduling algorithm PROCESS or SYSTEM */	if (pthread_attr_setscope( &attr, PTHREAD_SCOPE_PROCESS ) != 0)		if(verbose) 			printf( "unable to set to PTHREAD_SCOPE_PROCESS\n" );	/* set the scheduling policy - FIFO, RT, or OTHER */	if (pthread_attr_setschedpolicy( &attr, SCHED_OTHER ) != 0)		if(verbose) 			printf( "unable to set scheduling policy to SCHED_OTHER\n" );	/* get some scheduling priority information */	struct sched_param thread_param;	/* for scheduling */	if (pthread_attr_getschedparam( &attr, &thread_param ) != 0) {		if(verbose) {			printf( "unable to get scheduling info" );		}		} else {		int min_pri = sched_get_priority_min( SCHED_OTHER );		thread_param.sched_priority = min_pri;		pthread_attr_setschedparam( &attr, &thread_param ); 	}#else	if(verbose)		printf( "_POSIX_THREAD_PRIORITY_SCHEDULING undefined\n" );#endif	/* create the thread */	pthread_create( &tid, &attr, &scanThread, (void *)db );}////////////// howl (rendezvous, zeroconf, service discovery, mDNS-SD)#ifdef HOWL_ENABLEstatic sw_result HOWL_APIreplyServiceCallback(	sw_discovery			discovery,	sw_discovery_oid		oid,	sw_discovery_publish_status	status,	sw_opaque			extra){	/* 	static sw_string	status_text[] =	{		"Started",		"Stopped",		"Name Collision",		"Invalid"	};	fprintf(stderr, "publish reply: %s\n", status_text[status]);	*/		return SW_OKAY;}sw_discoverysetupServiceDiscovery( const char *name, const int port, const bool quiet ) {	sw_discovery			session;	sw_result			result;	sw_discovery_oid		oid;	sw_uint32			interface_index = 0; // SET THIS!	if (sw_discovery_init(&session) != SW_OKAY)	{		if( !quiet )			fprintf(stderr, "howl: sw_discovery_init() failed\n");		return NULL;	}	if ((result = sw_discovery_publish(		session, interface_index, name, "_daap._tcp", NULL, NULL, 		port, NULL, 0, replyServiceCallback, NULL, &oid)) != SW_OKAY)	{		if( !quiet )			fprintf(stderr, "howl: publish failed, result %d\n", result);		return NULL;	}		return session;}// intended to be called by pthread_createvoid *startServiceDiscovery( void *ptr ) {	sw_discovery *howlSessionPtr = (sw_discovery *)ptr;	sw_discovery_run( *howlSessionPtr );	return NULL;}#endif ////////////// mainint main ( int argc, char *argv[] ) {	 FILE *fp;	 char buf[512];	 sprintf((char *)&buf, "%s/.daapd.pid", getenv ("HOME"));	 if ((fp = fopen((char *)&buf, "w")) != NULL) {		 (void) fprintf(fp, "%d\n", getpid());		 (void) fclose(fp);	 }	httpd *server;	// initialization	InitParams initParams;	initParams = *getConfigFile( argc, argv, initParams );	initParams = *readConfig( initParams );	initParams = *parseArgs( argc, argv, initParams );	int buflen = 255;	char buffer[buflen];	if( gethostname( buffer, buflen ) == 0 ) {		if( initParams.serverName == std::string( "daapd server" ) )			initParams.serverName = (char *)buffer;		if( initParams.dbName == std::string( "daapd music" ) )			initParams.dbName = (char *)buffer;	}	if( initParams.dirs->size() == 0 ) {		std::string dir( "." );		initParams.dirs->push_back( dir );	}		// scan for audio files	if(!initParams.xmlparse)        	if (!initParams.quiet)	      		cout << "scanning " << initParams.dirs << " for audio files... " << flush;    	else        	cout << "Parsing XML file...." << flush;	Database db( initParams );	    if (!initParams.quiet)	      cout << "done initializing. " << endl;	// start http Server	errno = 0;	server = httpdCreate( NULL, initParams.port );	if ( server == NULL ) {		perror ( "Couldn't create HTTP server" );			exit(1);	}	httpdSetAccessLog( server, stdout );	httpdSetErrorLog( server, stderr ); 	// we do all URL parsing ourselves	httpdAddCSiteContent( server, parseRequest, (void*)&db );	httpdSetContentType( server, "application/x-dmap-tagged" );	httpdSetIdle( server, idleFunction, (void*)&db );	/* At this point we're fairly sure we're going to run just	   just fine - so we exit cleanly if we are in daemon	   mode. XXX - of course we really should switch any	   error logging and what not to syslog at this point.         */	if ( initParams.daemon ) 		startAsDaemon( initParams.verbose );		// start rescan thread	startScanThread( &db, initParams.verbose );		#ifdef HOWL_ENABLE	// start mDNS discovery (howl)	sw_discovery howlSession = setupServiceDiscovery( initParams.serverName.c_str(), initParams.port, initParams.quiet );	pthread_t         thread;	pthread_attr_t    threadAttribute;		if( howlSession != NULL ) {			pthread_attr_init( &threadAttribute );		pthread_create( &thread, &threadAttribute, 				&startServiceDiscovery, (void *) &howlSession);	}#endif		struct timeval timeout;		timeout.tv_sec  = 10;	timeout.tv_usec = 0;		// main event loop	while( true ) {  		httpdSelectLoop( server, timeout );	}	#ifdef HOWL_ENABLE	if( howlSession != NULL ) {		pthread_attr_destroy( &threadAttribute );	}	#endif	exit(0);}