#include #include #include #include #include #include #include #include #include #include "fbsh.h" std::string generateMD5 (char* input, int size) { int i; MD5_CTX md5; unsigned char out[MD5_DIGEST_LENGTH]; char txt[32]; std::string md5sum = ""; MD5_Init(&md5); MD5_Update(&md5, input, size); MD5_Final(out, &md5); for ( i = 0; i < MD5_DIGEST_LENGTH; i++) { snprintf (txt, 32, "%02x", out[i]); md5sum += txt; } return md5sum; } FBSmartHome::FBSmartHome() { hostname = ""; SID = "00 not yet set 00"; fsidfile = NULL; } FBSmartHome::~FBSmartHome() { if (fsidfile != NULL) CloseSIDFile(); } /* * try to connect with the last known SID. */ int FBSmartHome::Connect(std::string host) { int len; xmlDocPtr xmldoc; xmlNodePtr xmlnode; hostname = host; // is sid working? len = tcp.WebGetFile(hostname+"/login_sid.lua?sid="+SID, inbuffer, FB_BUFFER, NULL); if (len > FB_BUFFER) len = FB_BUFFER; else if (len < 0) { fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); errno = EAI_FAIL; return -1; } inbuffer[len] = '\0'; XMLPrepare(inbuffer, len, &xmldoc, &xmlnode); // // parse for SID and Challange while (xmlnode) { if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){ xmlNodePtr xmlchild = xmlnode->xmlChildrenNode; while (xmlchild) { if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID"))) SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1); xmlchild = xmlchild->next; } } xmlnode = xmlnode->next; } xmlFreeDoc(xmldoc); if (SID.compare("0000000000000000") == 0) { hostname = ""; errno = ENOTCONN; return -1; } errno = 0; return 0; } int FBSmartHome::XMLPrepare(char *buffer, int len, xmlDocPtr *xmldoc, xmlNodePtr *xmlnode) { // // read the xml data from memory and select root node. if (xmldoc == NULL || xmlnode == NULL) return -1; *xmldoc = xmlReadMemory(inbuffer, len, "hostname", NULL, 0); if (xmldoc == NULL) { fprintf (stderr, "%s: Could not read XML Document\n", __FUNCTION__); errno = EAI_FAIL; return -1; } *xmlnode = xmlDocGetRootElement(*xmldoc); if (*xmlnode == NULL) { fprintf (stderr, "%s: Empty XML Document\n", __FUNCTION__); xmlFreeDoc(*xmldoc); errno = EOF; return -1; } return 0; } /* * connect with username and password, if connect is successfull the * SID will be returned. */ #define PWSSIZE 512 int FBSmartHome::Connect (std::string host, std::string username, std::string password) { int len; xmlDocPtr xmldoc; xmlNodePtr xmlnode; std::string challenge; std::string formdata; hostname = host; SID = ""; tcp.Close(); // // get challenge from FritzBox len = tcp.WebGetFile(hostname+"/login_sid.lua", inbuffer, FB_BUFFER, NULL); if (len > FB_BUFFER) len = FB_BUFFER; else if (len < 0) { fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); errno = EAI_FAIL; return -1; } inbuffer[len] = '\0'; XMLPrepare(inbuffer, len, &xmldoc, &xmlnode); // // parse for SID and Challange while (xmlnode) { if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){ xmlNodePtr xmlchild = xmlnode->xmlChildrenNode; while (xmlchild) { if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID"))) SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1); else if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"Challenge"))) challenge = (char*) xmlNodeListGetString(xmldoc, xmlchild->children, 1); xmlchild = xmlchild->next; } } xmlnode = xmlnode->next; } xmlFreeDoc(xmldoc); // // create Challenge-PSW -> MD5 sum // // // convert to UTF16_LE std::string convertfrom = challenge+"-"+password; char *conv_input = (char *)convertfrom.c_str(); size_t conv_inbytes = strlen (conv_input); size_t conv_size; char convertedpsw[PWSSIZE]; char *conv_output = (char*)convertedpsw; size_t conv_outbytes = PWSSIZE; iconv_t ic = iconv_open("UTF-16LE", "UTF-8"); if ((long int)ic == -1) { fprintf (stderr, "iconv_open error: %s\n", strerror(errno)); return -1; } conv_size = iconv(ic, &conv_input, &conv_inbytes, (char**)&conv_output, &conv_outbytes); if ((long int)conv_size == -1) { fprintf (stderr, "iconv error: %s\n", strerror(errno)); return -1; } iconv_close(ic); // // create password formdata = "response="+challenge+"-"+generateMD5(convertedpsw, conv_output-convertedpsw)+"&username="+username; len = tcp.WebGetFile(hostname+"/login_sid.lua", inbuffer, FB_BUFFER, (char*)formdata.c_str()); if (len > FB_BUFFER) len = FB_BUFFER; else if (len < 0) { errno = EAI_FAIL; return -1; } inbuffer[len] = '\0'; // // parse for SID and Challange XMLPrepare(inbuffer, len, &xmldoc, &xmlnode); while (xmlnode) { if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){ xmlNodePtr xmlchild = xmlnode->xmlChildrenNode; while (xmlchild) { if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID"))) SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1); else if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"Challenge"))) challenge = (char*) xmlNodeListGetString(xmldoc, xmlchild->children, 1); xmlchild = xmlchild->next; } } xmlnode = xmlnode->next; } xmlFreeDoc(xmldoc); errno = 0; return 0; }; /* * read sidfile, if it does not exist create file. */ #define BUFSIZE 1024 int FBSmartHome::UseSIDFile(std::string fn) { char buffer[BUFSIZE]; int i; if ((fsidfile = fopen (fn.c_str(), "r+")) == NULL) { // file not found create file if ((fsidfile = fopen (fn.c_str(), "w")) == NULL) { fprintf (stderr, "error on writing sidfile '%s' : %s\n", fn.c_str(), strerror(errno)); return -1; } } else { // sidfile found and open fseek (fsidfile, 0, SEEK_SET); if (fgets(buffer, BUFSIZE, fsidfile) != NULL) { buffer[BUFSIZE] = '\0'; i = strlen (buffer); if (i > 0 && buffer[i-1] == '\n') buffer[i-1] = '\0'; SID = buffer; } } return 0; } /* * if the sid file is open save current sid. */ int FBSmartHome::CloseSIDFile() { if (fsidfile != NULL) { fseek (fsidfile, 0, SEEK_SET); fprintf (fsidfile, "%s", SID.c_str()); fclose(fsidfile); fsidfile = NULL; } return 0; } /* * retrieve the device list from the fritzbox */ std::list *FBSmartHome::GetDevices() { std::list *devlist = NULL; FBSmartHomeDevice smd; xmlDocPtr xmldoc; xmlNodePtr xmlnode; xmlAttr *xmlattr; int len; // retrieve data len = tcp.WebGetFile(hostname+"/webservices/homeautoswitch.lua?sid="+SID+"&switchcmd=getdevicelistinfos", inbuffer, FB_BUFFER, NULL); if (len > FB_BUFFER) len = FB_BUFFER; else if (len < 0) { fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__); errno = EAI_FAIL; return NULL; } inbuffer[len] = '\0'; XMLPrepare(inbuffer, len, &xmldoc, &xmlnode); printf ("*********************************************\n%s\n*********************************\n", inbuffer); // // parse for SID and Challange while (xmlnode) { if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"devicelist"))){ xmlNodePtr xmlchild = xmlnode->xmlChildrenNode; // // read each device details // while (xmlchild) { printf ("Name:%s\n", xmlchild->name); // // read attributes xmlattr = xmlchild->properties; while (xmlattr) { printf (" %s:%s ", xmlattr->name, xmlNodeListGetString(xmldoc, xmlattr->children, 1)); xmlattr = xmlattr->next; } printf ("\n"); // go thought all the subchildreen xmlNodePtr xmlsubchild = xmlchild->children; while (xmlsubchild) { printf (" %s,%s ", xmlsubchild->name, (char *)xmlNodeListGetString(xmldoc, xmlsubchild->children, 1)); xmlattr = xmlsubchild->properties; while (xmlattr) { printf (" %s:%s ", xmlattr->name, xmlNodeListGetString(xmldoc, xmlattr->children, 1)); xmlattr = xmlattr->next; } printf ("\n"); xmlNodePtr xmlsubchild2 = xmlsubchild->children; while (xmlsubchild2) { printf (" %s,%s ", xmlsubchild2->name, (char *)xmlNodeListGetString(xmldoc, xmlsubchild2->children, 1)); xmlattr = xmlsubchild2->properties; while (xmlattr) { printf (" %s:%s ", xmlattr->name, xmlNodeListGetString(xmldoc, xmlattr->children, 1)); xmlattr = xmlattr->next; } printf ("\n"); xmlsubchild2 = xmlsubchild2->next; } xmlsubchild = xmlsubchild->next; } printf ("\n"); xmlchild = xmlchild->next; } } xmlnode = xmlnode->next; } xmlFreeDoc(xmldoc); if (SID.compare("0000000000000000") == 0) { hostname = ""; errno = ENOTCONN; return NULL; } return devlist; }