]> git.vomp.tv Git - vompserver.git/blob - mvpclient.c
MVP Hx directory locator service added
[vompserver.git] / mvpclient.c
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "mvpclient.h"
22
23 // This is here else it causes compile errors with something in libdvbmpeg
24 #include <vdr/menu.h>
25
26 MVPClient::MVPClient(char* tconfigDirExtra, int tsocket)
27  : tcp(tsocket)
28 {
29   lp = NULL;
30   rp = NULL;
31   recordingManager = NULL;
32   log = Log::getInstance();
33   loggedIn = false;
34   configDirExtra = tconfigDirExtra;
35 }
36
37 MVPClient::~MVPClient()
38 {
39   log->log("Client", Log::DEBUG, "MVP client destructor");
40   if (lp)
41   {
42     delete lp;
43     lp = NULL;
44   }
45   else if (rp)
46   {
47     writeResumeData();
48
49     delete rp;
50     delete recordingManager;
51     rp = NULL;
52     recordingManager = NULL;
53   }
54
55   if (loggedIn) cleanConfig();
56 }
57
58 ULLONG MVPClient::ntohll(ULLONG a)
59 {
60   return htonll(a);
61 }
62
63 ULLONG MVPClient::htonll(ULLONG a)
64 {
65   #if BYTE_ORDER == BIG_ENDIAN
66     return a;
67   #else
68     ULLONG b = 0;
69
70     b = ((a << 56) & 0xFF00000000000000ULL)
71       | ((a << 40) & 0x00FF000000000000ULL)
72       | ((a << 24) & 0x0000FF0000000000ULL)
73       | ((a <<  8) & 0x000000FF00000000ULL)
74       | ((a >>  8) & 0x00000000FF000000ULL)
75       | ((a >> 24) & 0x0000000000FF0000ULL)
76       | ((a >> 40) & 0x000000000000FF00ULL)
77       | ((a >> 56) & 0x00000000000000FFULL) ;
78
79     return b;
80   #endif
81 }
82
83 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
84 {
85   cChannel* channel = NULL;
86
87   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
88   {
89     if (!channel->GroupSep())
90     {
91       log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
92
93       if (channel->Number() == (int)channelNumber)
94       {
95         int vpid = channel->Vpid();
96 #if VDRVERSNUM < 10300
97         int apid1 = channel->Apid1();
98 #else
99         int apid1 = channel->Apid(0);
100 #endif
101         log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
102         return channel;
103       }
104     }
105   }
106
107   if (!channel)
108   {
109     log->log("Client", Log::DEBUG, "Channel not found");
110   }
111
112   return channel;
113 }
114
115 void MVPClient::writeResumeData()
116 {
117   config.setValueLong("ResumeData",
118                           (char*)rp->getCurrentRecording()->FileName(),
119                           rp->frameNumberFromPosition(rp->getLastPosition()) );
120 }
121
122 void MVPClient::sendULONG(ULONG ul)
123 {
124   UCHAR sendBuffer[8];
125   *(ULONG*)&sendBuffer[0] = htonl(4);
126   *(ULONG*)&sendBuffer[4] = htonl(ul);
127
128   tcp.sendPacket(sendBuffer, 8);
129   log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
130 }
131
132 void MVPClientStartThread(void* arg)
133 {
134   MVPClient* m = (MVPClient*)arg;
135   m->run2();
136   // Nothing external to this class has a reference to it
137   // This is the end of the thread.. so delete m
138   delete m;
139   pthread_exit(NULL);
140 }
141
142 int MVPClient::run()
143 {
144   if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
145   log->log("Client", Log::DEBUG, "MVPClient run success");
146   return 1;
147 }
148
149 void MVPClient::run2()
150 {
151   // Thread stuff
152   sigset_t sigset;
153   sigfillset(&sigset);
154   pthread_sigmask(SIG_BLOCK, &sigset, NULL);
155   pthread_detach(runThread);  // Detach
156
157   tcp.disableReadTimeout();
158
159   tcp.setSoKeepTime(3);
160   tcp.setNonBlocking();
161
162   UCHAR* buffer;
163   UCHAR* data;
164   int packetLength;
165   ULONG opcode;
166   int result = 0;
167
168   while(1)
169   {
170     log->log("Client", Log::DEBUG, "Waiting");
171     buffer = (UCHAR*)tcp.receivePacket();
172     log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
173     if (buffer == NULL)
174     {
175       log->log("Client", Log::DEBUG, "Detected connection closed");
176       break;
177     }
178
179     packetLength = tcp.getDataLength() - 4;
180     opcode = ntohl(*(ULONG*)buffer);
181     data = buffer + 4;
182
183     if (!loggedIn && (opcode != 1))
184     {
185       free(buffer);
186       break;
187     }
188
189     log->log("Client", Log::DEBUG, "SwitchOp");
190     switch(opcode)
191     {
192       case 1:
193         result = processLogin(data, packetLength);
194         break;
195       case 2:
196         result = processGetRecordingsList(data, packetLength);
197         break;
198       case 3:
199         result = processDeleteRecording(data, packetLength);
200         break;
201       case 5:
202         result = processGetChannelsList(data, packetLength);
203         break;
204       case 6:
205         result = processStartStreamingChannel(data, packetLength);
206         break;
207       case 7:
208         result = processGetBlock(data, packetLength);
209         break;
210       case 8:
211         result = processStopStreaming(data, packetLength);
212         break;
213       case 9:
214         result = processStartStreamingRecording(data, packetLength);
215         break;
216       case 10:
217         result = processGetChannelSchedule(data, packetLength);
218         break;
219       case 11:
220         result = processConfigSave(data, packetLength);
221         break;
222       case 12:
223         result = processConfigLoad(data, packetLength);
224         break;
225       case 13:
226         result = processReScanRecording(data, packetLength);         // FIXME obselete
227         break;
228       case 14:
229         result = processGetTimers(data, packetLength);
230         break;
231       case 15:
232         result = processSetTimer(data, packetLength);
233         break;
234       case 16:
235         result = processPositionFromFrameNumber(data, packetLength);
236         break;
237       case 17:
238         result = processFrameNumberFromPosition(data, packetLength);
239         break;
240       case 18:
241         result = processMoveRecording(data, packetLength);
242         break;
243       case 19:
244         result = processGetIFrame(data, packetLength);
245         break;
246       case 20:
247         result = processGetRecInfo(data, packetLength);
248         break;
249       case 21:
250         result = processGetMarks(data, packetLength);
251         break;
252       case 22:
253         result = processGetChannelPids(data, packetLength);
254         break;
255     }
256
257     free(buffer);
258     if (!result) break;
259   }
260 }
261
262 int MVPClient::processLogin(UCHAR* buffer, int length)
263 {
264   if (length != 6) return 0;
265
266   // Open the config
267
268   const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
269   if (!configDir)
270   {
271     log->log("Client", Log::DEBUG, "No config dir!");
272     return 0;
273   }
274
275   char configFileName[PATH_MAX];
276   snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
277   config.init(configFileName);
278
279   // Send the login reply
280
281   time_t timeNow = time(NULL);
282   struct tm* timeStruct = localtime(&timeNow);
283   int timeOffset = timeStruct->tm_gmtoff;
284
285   UCHAR sendBuffer[12];
286   *(ULONG*)&sendBuffer[0] = htonl(8);
287   *(ULONG*)&sendBuffer[4] = htonl(timeNow);
288   *(signed int*)&sendBuffer[8] = htonl(timeOffset);
289
290   tcp.sendPacket(sendBuffer, 12);
291   log->log("Client", Log::DEBUG, "written login reply");
292
293   loggedIn = true;
294   return 1;
295 }
296
297 int MVPClient::processGetRecordingsList(UCHAR* data, int length)
298 {
299   UCHAR* sendBuffer = new UCHAR[50000]; // hope this is enough
300   int count = 4; // leave space for the packet length
301   char* point;
302
303
304   int FreeMB;
305   int Percent = VideoDiskSpace(&FreeMB);
306   int Total = (FreeMB / (100 - Percent)) * 100;
307
308   *(ULONG*)&sendBuffer[count] = htonl(Total);
309   count += sizeof(ULONG);
310   *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
311   count += sizeof(ULONG);
312   *(ULONG*)&sendBuffer[count] = htonl(Percent);
313   count += sizeof(ULONG);
314
315
316   cRecordings Recordings;
317   Recordings.Load();
318
319   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
320   {
321     if (count > 49000) break; // just how big is that hard disk?!
322     *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
323     count += 4;
324
325     point = (char*)recording->Name();
326     strcpy((char*)&sendBuffer[count], point);
327     count += strlen(point) + 1;
328
329     point = (char*)recording->FileName();
330     strcpy((char*)&sendBuffer[count], point);
331     count += strlen(point) + 1;
332   }
333
334   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
335
336   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
337
338   tcp.sendPacket(sendBuffer, count);
339   delete[] sendBuffer;
340   log->log("Client", Log::DEBUG, "Written list");
341
342   return 1;
343 }
344
345 int MVPClient::processDeleteRecording(UCHAR* data, int length)
346 {
347   // data is a pointer to the fileName string
348
349   cRecordings Recordings;
350   Recordings.Load(); // probably have to do this
351
352   cRecording* recording = Recordings.GetByName((char*)data);
353
354   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
355
356   if (recording)
357   {
358     log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
359
360     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
361     if (!rc)
362     {
363       if (recording->Delete())
364       {
365         // Copy svdrp's way of doing this, see if it works
366 #if VDRVERSNUM > 10300
367         ::Recordings.DelByName(recording->FileName());
368 #endif
369         sendULONG(1);
370       }
371       else
372       {
373         sendULONG(2);
374       }
375     }
376     else
377     {
378       sendULONG(3);
379     }
380   }
381   else
382   {
383     sendULONG(4);
384   }
385
386   return 1;
387 }
388
389 int MVPClient::processMoveRecording(UCHAR* data, int length)
390 {
391   log->log("Client", Log::DEBUG, "Process move recording");
392   char* fileName = (char*)data;
393   char* newPath = NULL;
394
395   for (int k = 0; k < length; k++)
396   {
397     if (data[k] == '\0')
398     {
399       newPath = (char*)&data[k+1];
400       break;
401     }
402   }
403   if (!newPath) return 0;
404
405   cRecordings Recordings;
406   Recordings.Load(); // probably have to do this
407
408   cRecording* recording = Recordings.GetByName((char*)fileName);
409
410   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
411
412   if (recording)
413   {
414     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
415     if (!rc)
416     {
417       log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
418       log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
419       log->log("Client", Log::DEBUG, "to: %s", newPath);
420
421       const char* t = recording->FileName();
422
423       char* dateDirName = NULL;   int k;
424       char* titleDirName = NULL;  int j;
425
426       // Find the datedirname
427       for(k = strlen(t) - 1; k >= 0; k--)
428       {
429         if (t[k] == '/')
430         {
431           log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
432           dateDirName = new char[strlen(&t[k+1]) + 1];
433           strcpy(dateDirName, &t[k+1]);
434           break;
435         }
436       }
437
438       // Find the titledirname
439
440       for(j = k-1; j >= 0; j--)
441       {
442         if (t[j] == '/')
443         {
444           log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
445           titleDirName = new char[(k - j - 1) + 1];
446           memcpy(titleDirName, &t[j+1], k - j - 1);
447           titleDirName[k - j - 1] = '\0';
448           break;
449         }
450       }
451
452       log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
453       log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
454
455       log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
456
457       char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
458       log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
459       sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
460
461       // FIXME Check whether this already exists before mkdiring it
462
463       log->log("Client", Log::DEBUG, "%s", newContainer);
464
465
466       struct stat dstat;
467       int statret = stat(newContainer, &dstat);
468       if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
469       {
470         log->log("Client", Log::DEBUG, "new dir does not exist");
471         int mkdirret = mkdir(newContainer, 0755);
472         if (mkdirret != 0)
473         {
474           delete[] dateDirName;
475           delete[] titleDirName;
476           delete[] newContainer;
477           sendULONG(5);
478           return 1;
479         }
480       }
481       else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
482       {
483         delete[] dateDirName;
484         delete[] titleDirName;
485         delete[] newContainer;
486         sendULONG(5);
487         return 1;
488       }
489
490       // Ok, the directory container has been made, or it pre-existed.
491
492       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
493       sprintf(newDir, "%s/%s", newContainer, dateDirName);
494
495       log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
496       int renameret = rename(t, newDir);
497       if (renameret == 0)
498       {
499         // Success. Test for remove old dir containter
500         char* oldTitleDir = new char[k+1];
501         memcpy(oldTitleDir, t, k);
502         oldTitleDir[k] = '\0';
503         log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
504         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
505         delete[] oldTitleDir;
506       }
507
508       if (renameret == 0)
509       {
510 #if VDRVERSNUM > 10311
511         // Tell VDR
512         ::Recordings.Update();
513 #endif
514         // Success. Send a different packet from just a ulong
515         int totalLength = 4 + 4 + strlen(newDir) + 1;
516         UCHAR* sendBuffer = new UCHAR[totalLength];
517         *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
518         *(ULONG*)&sendBuffer[4] = htonl(1); // success
519         strcpy((char*)&sendBuffer[8], newDir);
520         tcp.sendPacket(sendBuffer, totalLength);
521         delete[] sendBuffer;
522       }
523       else
524       {
525         sendULONG(5);
526       }
527
528       delete[] dateDirName;
529       delete[] titleDirName;
530       delete[] newContainer;
531       delete[] newDir;
532     }
533     else
534     {
535       sendULONG(3);
536     }
537   }
538   else
539   {
540     sendULONG(4);
541   }
542
543   return 1;
544 }
545
546 int MVPClient::processGetChannelsList(UCHAR* data, int length)
547 {
548   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
549   int count = 4; // leave space for the packet length
550   char* point;
551   ULONG type;
552
553   char* chanConfig = config.getValueString("General", "Channels");
554   int allChans = 1;
555   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
556
557   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
558   {
559 #if VDRVERSNUM < 10300
560     if (!channel->GroupSep() && (!channel->Ca() || allChans))
561 #else
562     if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
563 #endif
564     {
565       log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
566
567       if (channel->Vpid()) type = 1;
568 #if VDRVERSNUM < 10300
569       else type = 2;
570 #else
571       else if (channel->Apid(0)) type = 2;
572       else continue;
573 #endif
574
575       if (count > 49000) break;
576       *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
577       count += 4;
578
579       *(ULONG*)&sendBuffer[count] = htonl(type);
580       count += 4;
581
582       point = (char*)channel->Name();
583       strcpy((char*)&sendBuffer[count], point);
584       count += strlen(point) + 1;
585     }
586   }
587
588   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
589
590   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
591
592   tcp.sendPacket(sendBuffer, count);
593   delete[] sendBuffer;
594   log->log("Client", Log::DEBUG, "Written channels list");
595
596   return 1;
597 }
598
599 int MVPClient::processGetChannelPids(UCHAR* data, int length)
600 {
601   ULONG channelNumber = ntohl(*(ULONG*)data);
602
603   cChannel* channel = channelFromNumber(channelNumber);
604   if (!channel)
605   {
606     sendULONG(0);
607     return 1;
608   }
609
610   ULONG numApids = 0;
611   ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
612   // Work out space required and number of Apids
613   for (const int *Apid = channel->Apids(); *Apid; Apid++)
614   {
615     spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
616     numApids++;
617   }
618
619   // Format of response
620   // vpid
621   // number of apids
622   // {
623   //    apid
624   //    lang string
625   // }
626
627   UCHAR* sendBuffer = new UCHAR[spaceRequired];
628   ULONG point = 0;
629   *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4);   point += 4;   // take off first 4 bytes
630   *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid());     point += 4;
631   *(ULONG*)&sendBuffer[point] = htonl(numApids);            point += 4;
632
633   for (ULONG i = 0; i < numApids; i++)
634   {
635     *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i));  point += 4;
636     strcpy((char*)&sendBuffer[point], channel->Alang(i));          point += strlen(channel->Alang(i)) + 1;
637   }
638
639   printf("About to send getchannelpids response. length = %u\n", spaceRequired);
640   tcp.dump(sendBuffer, spaceRequired);
641
642   tcp.sendPacket(sendBuffer, spaceRequired);
643   delete[] sendBuffer;
644   log->log("Client", Log::DEBUG, "Written channels pids");
645
646   return 1;
647 }
648
649 int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
650 {
651   log->log("Client", Log::DEBUG, "length = %i", length);
652   ULONG channelNumber = ntohl(*(ULONG*)data);
653
654   cChannel* channel = channelFromNumber(channelNumber);
655   if (!channel)
656   {
657     sendULONG(0);
658     return 1;
659   }
660
661   // get the priority we should use
662   int fail = 1;
663   int priority = config.getValueLong("General", "Live priority", &fail);
664   if (!fail)
665   {
666     log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
667   }
668   else
669   {
670     log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
671     priority = 0;
672   }
673
674   // a bit of sanity..
675   if (priority < 0) priority = 0;
676   if (priority > 99) priority = 99;
677
678   log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
679   lp = MVPReceiver::create(channel, priority);
680
681   if (!lp)
682   {
683     sendULONG(0);
684     return 1;
685   }
686
687   if (!lp->init())
688   {
689     delete lp;
690     lp = NULL;
691     sendULONG(0);
692     return 1;
693   }
694
695   sendULONG(1);
696   return 1;
697 }
698
699 int MVPClient::processStopStreaming(UCHAR* data, int length)
700 {
701   log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
702   if (lp)
703   {
704     delete lp;
705     lp = NULL;
706   }
707   else if (rp)
708   {
709     writeResumeData();
710
711     delete rp;
712     delete recordingManager;
713     rp = NULL;
714     recordingManager = NULL;
715   }
716
717   sendULONG(1);
718   return 1;
719 }
720
721 int MVPClient::processGetBlock(UCHAR* data, int length)
722 {
723   if (!lp && !rp)
724   {
725     log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
726     return 0;
727   }
728
729   ULLONG position = ntohll(*(ULLONG*)data);
730   data += sizeof(ULLONG);
731   ULONG amount = ntohl(*(ULONG*)data);
732
733   log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
734
735   UCHAR sendBuffer[amount + 4];
736   ULONG amountReceived = 0; // compiler moan.
737   if (lp)
738   {
739     log->log("Client", Log::DEBUG, "getting from live");
740     amountReceived = lp->getBlock(&sendBuffer[4], amount);
741
742     if (!amountReceived)
743     {
744       // vdr has possibly disconnected the receiver
745       log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
746       delete lp;
747       lp = NULL;
748     }
749   }
750   else if (rp)
751   {
752     log->log("Client", Log::DEBUG, "getting from recording");
753     amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
754   }
755
756   if (!amountReceived)
757   {
758     sendULONG(0);
759     log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
760   }
761   else
762   {
763     *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
764     tcp.sendPacket(sendBuffer, amountReceived + 4);
765     log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
766   }
767
768   return 1;
769 }
770
771 int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
772 {
773   // data is a pointer to the fileName string
774
775   recordingManager = new cRecordings;
776   recordingManager->Load();
777
778   cRecording* recording = recordingManager->GetByName((char*)data);
779
780   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
781
782   if (recording)
783   {
784     rp = new RecPlayer(recording);
785
786     UCHAR sendBuffer[16];
787     *(ULONG*)&sendBuffer[0] = htonl(12);
788     *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
789     *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
790
791     tcp.sendPacket(sendBuffer, 16);
792     log->log("Client", Log::DEBUG, "written totalLength");
793   }
794   else
795   {
796     delete recordingManager;
797     recordingManager = NULL;
798   }
799   return 1;
800 }
801
802 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
803 {
804   ULLONG retval = 0;
805
806   ULONG frameNumber = ntohl(*(ULONG*)data);
807   data += 4;
808
809   if (!rp)
810   {
811     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
812   }
813   else
814   {
815     retval = rp->positionFromFrameNumber(frameNumber);
816   }
817
818   UCHAR sendBuffer[12];
819   *(ULONG*)&sendBuffer[0] = htonl(8);
820   *(ULLONG*)&sendBuffer[4] = htonll(retval);
821
822   tcp.sendPacket(sendBuffer, 12);
823   log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
824   return 1;
825 }
826
827 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
828 {
829   ULONG retval = 0;
830
831   ULLONG position = ntohll(*(ULLONG*)data);
832   data += 8;
833
834   if (!rp)
835   {
836     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
837   }
838   else
839   {
840     retval = rp->frameNumberFromPosition(position);
841   }
842
843   UCHAR sendBuffer[8];
844   *(ULONG*)&sendBuffer[0] = htonl(4);
845   *(ULONG*)&sendBuffer[4] = htonl(retval);
846
847   tcp.sendPacket(sendBuffer, 8);
848   log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
849   return 1;
850 }
851
852 int MVPClient::processGetIFrame(UCHAR* data, int length)
853 {
854   bool success = false;
855
856   ULONG frameNumber = ntohl(*(ULONG*)data);
857   data += 4;
858   ULONG direction = ntohl(*(ULONG*)data);
859   data += 4;
860
861   ULLONG rfilePosition = 0;
862   ULONG rframeNumber = 0;
863   ULONG rframeLength = 0;
864
865   if (!rp)
866   {
867     log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
868   }
869   else
870   {
871     success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
872   }
873
874   // returns file position, frame number, length
875
876   UCHAR sendBuffer[20];
877   int packetLength;
878
879   if (success)
880   {
881     packetLength = 20;
882     *(ULONG*)&sendBuffer[0] = htonl(16);
883     *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
884     *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
885     *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
886   }
887   else
888   {
889     packetLength = 8;
890     *(ULONG*)&sendBuffer[0] = htonl(4);
891     *(ULONG*)&sendBuffer[4] = 0;
892   }
893
894   log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
895
896   tcp.sendPacket(sendBuffer, packetLength);
897   log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
898   return 1;
899 }
900
901 int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
902 {
903   ULONG channelNumber = ntohl(*(ULONG*)data);
904   data += 4;
905   ULONG startTime = ntohl(*(ULONG*)data);
906   data += 4;
907   ULONG duration = ntohl(*(ULONG*)data);
908
909   log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
910
911   cChannel* channel = channelFromNumber(channelNumber);
912   if (!channel)
913   {
914     sendULONG(0);
915     log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
916     return 1;
917   }
918
919   log->log("Client", Log::DEBUG, "Got channel");
920
921 #if VDRVERSNUM < 10300
922   cMutexLock MutexLock;
923   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
924 #else
925   cSchedulesLock MutexLock;
926   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
927 #endif
928   if (!Schedules)
929   {
930     sendULONG(0);
931     log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
932     return 1;
933   }
934
935   log->log("Client", Log::DEBUG, "Got schedule!s! object");
936
937   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
938   if (!Schedule)
939   {
940     sendULONG(0);
941     log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
942     return 1;
943   }
944
945   log->log("Client", Log::DEBUG, "Got schedule object");
946
947   UCHAR* sendBuffer = (UCHAR*)malloc(100000);
948   ULONG sendBufferLength = 100000;
949   ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length
950
951   char* empty = "";
952
953   // assign all the event info to temp vars then we know exactly what size they are
954   ULONG thisEventID;
955   ULONG thisEventTime;
956   ULONG thisEventDuration;
957   const char* thisEventTitle;
958   const char* thisEventSubTitle;
959   const char* thisEventDescription;
960
961   ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
962   ULONG thisEventLength;
963
964 #if VDRVERSNUM < 10300
965
966   const cEventInfo *event;
967   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
968   {
969     event = Schedule->GetEventNumber(eventNumber);
970
971     thisEventID = event->GetEventID();
972     thisEventTime = event->GetTime();
973     thisEventDuration = event->GetDuration();
974     thisEventTitle = event->GetTitle();
975     thisEventSubTitle = event->GetSubtitle();
976     thisEventDescription = event->GetExtendedDescription();
977
978 #else
979
980   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
981   {
982     thisEventID = event->EventID();
983     thisEventTime = event->StartTime();
984     thisEventDuration = event->Duration();
985     thisEventTitle = event->Title();
986     thisEventSubTitle = NULL;
987     thisEventDescription = event->Description();
988
989 #endif
990
991     log->log("Client", Log::DEBUG, "Got an event object %p", event);
992
993     //in the past filter
994     if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
995
996     //start time filter
997     if ((thisEventTime + thisEventDuration) <= startTime) continue;
998
999     //duration filter
1000     if (thisEventTime >= (startTime + duration)) continue;
1001
1002     if (!thisEventTitle) thisEventTitle = empty;
1003     if (!thisEventSubTitle) thisEventSubTitle = empty;
1004     if (!thisEventDescription) thisEventDescription = empty;
1005
1006     thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;
1007
1008     log->log("Client", Log::DEBUG, "Done s1");
1009
1010     // now extend the buffer if necessary
1011     if ((sendBufferUsed + thisEventLength) > sendBufferLength)
1012     {
1013       log->log("Client", Log::DEBUG, "Extending buffer");
1014       sendBufferLength += 100000;
1015       UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
1016       if (temp == NULL)
1017       {
1018         free(sendBuffer);
1019         UCHAR sendBuffer2[8];
1020         *(ULONG*)&sendBuffer2[0] = htonl(4);
1021         *(ULONG*)&sendBuffer2[4] = htonl(0);
1022         tcp.sendPacket(sendBuffer2, 8);
1023         log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
1024         return 1;
1025       }
1026       sendBuffer = temp;
1027     }
1028
1029     log->log("Client", Log::DEBUG, "Done s2");
1030
1031     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
1032     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
1033     *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);
1034
1035     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
1036     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
1037     strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;
1038
1039     log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
1040   }
1041
1042   log->log("Client", Log::DEBUG, "Got all event data");
1043
1044   if (sendBufferUsed == sizeof(ULONG))
1045   {
1046     // No data
1047     sendULONG(0);
1048     log->log("Client", Log::DEBUG, "Written 0 because no data");
1049   }
1050   else
1051   {
1052     // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
1053     *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
1054     tcp.sendPacket(sendBuffer, sendBufferUsed);
1055     log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
1056   }
1057
1058   free(sendBuffer);
1059
1060   return 1;
1061 }
1062
1063 int MVPClient::processConfigSave(UCHAR* buffer, int length)
1064 {
1065   char* section = (char*)buffer;
1066   char* key = NULL;
1067   char* value = NULL;
1068
1069   for (int k = 0; k < length; k++)
1070   {
1071     if (buffer[k] == '\0')
1072     {
1073       if (!key)
1074       {
1075         key = (char*)&buffer[k+1];
1076       }
1077       else
1078       {
1079         value = (char*)&buffer[k+1];
1080         break;
1081       }
1082     }
1083   }
1084
1085   // if the last string (value) doesnt have null terminator, give up
1086   if (buffer[length - 1] != '\0') return 0;
1087
1088   log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1089   if (config.setValueString(section, key, value))
1090   {
1091     sendULONG(1);
1092   }
1093   else
1094   {
1095     sendULONG(0);
1096   }
1097
1098   return 1;
1099 }
1100
1101 int MVPClient::processConfigLoad(UCHAR* buffer, int length)
1102 {
1103   char* section = (char*)buffer;
1104   char* key = NULL;
1105
1106   for (int k = 0; k < length; k++)
1107   {
1108     if (buffer[k] == '\0')
1109     {
1110       key = (char*)&buffer[k+1];
1111       break;
1112     }
1113   }
1114
1115   char* value = config.getValueString(section, key);
1116
1117   if (value)
1118   {
1119     UCHAR sendBuffer[4 + strlen(value) + 1];
1120     *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
1121     strcpy((char*)&sendBuffer[4], value);
1122     tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);
1123
1124     log->log("Client", Log::DEBUG, "Written config load packet");
1125     delete[] value;
1126   }
1127   else
1128   {
1129     UCHAR sendBuffer[8];
1130     *(ULONG*)&sendBuffer[0] = htonl(4);
1131     *(ULONG*)&sendBuffer[4] = htonl(0);
1132     tcp.sendPacket(sendBuffer, 8);
1133
1134     log->log("Client", Log::DEBUG, "Written config load failed packet");
1135   }
1136
1137   return 1;
1138 }
1139
1140 void MVPClient::cleanConfig()
1141 {
1142   log->log("Client", Log::DEBUG, "Clean config");
1143
1144   cRecordings Recordings;
1145   Recordings.Load();
1146
1147   int numReturns;
1148   int length;
1149   char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
1150   char* position = resumes;
1151   for(int k = 0; k < numReturns; k++)
1152   {
1153     log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
1154
1155     cRecording* recording = Recordings.GetByName(position);
1156     if (!recording)
1157     {
1158       // doesn't exist anymore
1159       log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
1160       config.deleteValue("ResumeData", position);
1161     }
1162     else
1163     {
1164       log->log("Client", Log::DEBUG, "This recording still exists");
1165     }
1166
1167     position += strlen(position) + 1;
1168   }
1169
1170   delete[] resumes;
1171 }
1172
1173
1174
1175
1176
1177
1178 /*
1179     event = Schedule->GetPresentEvent();
1180
1181     fprintf(f, "\n\nCurrent event\n\n");
1182
1183     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1184     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1185     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1186     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1187
1188     event = Schedule->GetFollowingEvent();
1189
1190     fprintf(f, "\n\nFollowing event\n\n");
1191
1192     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1193     fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1194     fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1195     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1196
1197     fprintf(f, "\n\n");
1198 */
1199
1200 /*
1201     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1202     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1203     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1204     fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1205
1206     fprintf(f, "\n\n");
1207 */
1208
1209 /*
1210
1211
1212 void MVPClient::test2()
1213 {
1214   FILE* f = fopen("/tmp/s.txt", "w");
1215
1216 #if VDRVERSNUM < 10300
1217   cMutexLock MutexLock;
1218   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1219 #else
1220   cSchedulesLock MutexLock;
1221   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1222 #endif
1223
1224   if (!Schedules)
1225   {
1226     fprintf(f, "Schedules = NULL\n");
1227     fclose(f);
1228     return;
1229   }
1230
1231   fprintf(f, "Schedules dump:\n");
1232   Schedules->Dump(f);
1233
1234
1235   const cSchedule *Schedule;
1236   int scheduleNumber = 0;
1237
1238   tChannelID tchid;
1239   cChannel *thisChannel;
1240
1241 #if VDRVERSNUM < 10300
1242   const cEventInfo *event;
1243   int eventNumber = 0;
1244 #else
1245   const cEvent *event;
1246 #endif
1247
1248 //    Schedule = Schedules->GetSchedule(channel->GetChannelID());
1249 //    Schedule = Schedules->GetSchedule();
1250   Schedule = Schedules->First();
1251   if (!Schedule)
1252   {
1253     fprintf(f, "First Schedule = NULL\n");
1254     fclose(f);
1255     return;
1256   }
1257
1258   while (Schedule)
1259   {
1260     fprintf(f, "Schedule #%i\n", scheduleNumber);
1261     fprintf(f, "-------------\n\n");
1262
1263 #if VDRVERSNUM < 10300
1264     tchid = Schedule->GetChannelID();
1265 #else
1266     tchid = Schedule->ChannelID();
1267 #endif
1268
1269 #if VDRVERSNUM < 10300
1270     fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1271     fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1272 #else
1273 //  put the count at the end.
1274 #endif
1275
1276     thisChannel = Channels.GetByChannelID(tchid, true);
1277     if (thisChannel)
1278     {
1279       fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1280     }
1281     else
1282     {
1283       fprintf(f, "thisChannel = NULL for tchid\n");
1284     }
1285
1286 #if VDRVERSNUM < 10300
1287     for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1288     {
1289       event = Schedule->GetEventNumber(eventNumber);
1290       fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1291       fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1292       fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1293       fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1294       fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1295       fprintf(f, "Event %u dump:\n", eventNumber);
1296       event->Dump(f);
1297       fprintf(f, "\n\n");
1298     }
1299 #else
1300 //  This whole section needs rewriting to walk the list.
1301     event = Schedule->Events()->First();
1302     while (event) {
1303       event = Schedule->Events()->Next(event);
1304     }
1305 #endif
1306
1307
1308     fprintf(f, "\nDump from object:\n");
1309     Schedule->Dump(f);
1310     fprintf(f, "\nEND\n");
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320     fprintf(f, "End of current Schedule\n\n\n");
1321
1322     Schedule = (const cSchedule *)Schedules->Next(Schedule);
1323     scheduleNumber++;
1324   }
1325
1326   fclose(f);
1327 }
1328
1329
1330
1331 */
1332
1333
1334
1335 /*
1336   const cEventInfo *GetPresentEvent(void) const;
1337   const cEventInfo *GetFollowingEvent(void) const;
1338   const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1339   const cEventInfo *GetEventAround(time_t tTime) const;
1340   const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
1341
1342
1343   const unsigned char GetTableID(void) const;
1344   const char *GetTimeString(void) const;
1345   const char *GetEndTimeString(void) const;
1346   const char *GetDate(void) const;
1347   bool IsFollowing(void) const;
1348   bool IsPresent(void) const;
1349   const char *GetExtendedDescription(void) const;
1350   const char *GetSubtitle(void) const;
1351   const char *GetTitle(void) const;
1352   unsigned short GetEventID(void) const;
1353   long GetDuration(void) const;
1354   time_t GetTime(void) const;
1355   tChannelID GetChannelID(void) const;
1356   int GetChannelNumber(void) const { return nChannelNumber; }
1357   void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
1358   void Dump(FILE *f, const char *Prefix = "") const;
1359
1360 */
1361
1362
1363 /*
1364 void MVPClient::test(int channelNumber)
1365 {
1366   FILE* f = fopen("/tmp/test.txt", "w");
1367
1368   cMutexLock MutexLock;
1369   const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1370
1371   if (!Schedules)
1372   {
1373     fprintf(f, "Schedules = NULL\n");
1374     fclose(f);
1375     return;
1376   }
1377
1378   fprintf(f, "Schedules dump:\n");
1379 //  Schedules->Dump(f);
1380
1381   const cSchedule *Schedule;
1382   cChannel *thisChannel;
1383   const cEventInfo *event;
1384
1385   thisChannel = channelFromNumber(channelNumber);
1386   if (!thisChannel)
1387   {
1388     fprintf(f, "thisChannel = NULL\n");
1389     fclose(f);
1390     return;
1391   }
1392
1393   Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
1394 //    Schedule = Schedules->GetSchedule();
1395 //  Schedule = Schedules->First();
1396   if (!Schedule)
1397   {
1398     fprintf(f, "First Schedule = NULL\n");
1399     fclose(f);
1400     return;
1401   }
1402
1403   fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
1404
1405   // For some channels VDR seems to pick a random point in time to
1406   // start dishing out events, but they are in order
1407   // at some point in the list the time snaps to the current event
1408
1409
1410
1411
1412   for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1413   {
1414     event = Schedule->GetEventNumber(eventNumber);
1415     fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1416     fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1417     fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1418     fprintf(f, "\n\n");
1419   }
1420
1421   fprintf(f, "\nEND\n");
1422
1423   fclose(f);
1424 }
1425
1426 */
1427
1428
1429
1430 /*
1431
1432
1433 Right, so
1434
1435 Schedules = the collection of all the Schedule objects
1436 Schedule  = One schedule, contants all the events for a channel
1437 Event     = One programme
1438
1439
1440 Want:
1441
1442 Event ID
1443 Time
1444 Duration
1445 Title
1446 Subtitle (used for "Programmes resume at ...")
1447 Description
1448
1449 IsPresent ? easy to work out tho. Oh it doesn't always work
1450
1451 */
1452
1453 /*
1454 void MVPClient::test2()
1455 {
1456   log->log("-", Log::DEBUG, "Timers List");
1457
1458   for (int i = 0; i < Timers.Count(); i++)
1459   {
1460     cTimer *timer = Timers.Get(i);
1461     //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
1462     log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
1463 #if VDRVERSNUM < 10300
1464     log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->Active(), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
1465 #else
1466     log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->HasFlags(tfActive), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
1467 #endif
1468     log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
1469     log->log("-", Log::DEBUG, "");
1470   }
1471
1472   // asprintf(&buffer, "%d:%s:%s  :%04d:%04d:%d:%d:%s:%s\n",
1473 //            active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
1474 //            PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
1475 }
1476 */
1477
1478 /*
1479 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
1480 recording is a bool, 0 for not currently recording, 1 for currently recording
1481 pending is a bool, 0 for would not be trying to record this right now, 1 for would/is trying to record this right now
1482 */
1483
1484
1485 int MVPClient::processGetTimers(UCHAR* buffer, int length)
1486 {
1487   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1488   int count = 4; // leave space for the packet length
1489
1490   const char* fileName;
1491   cTimer *timer;
1492   int numTimers = Timers.Count();
1493
1494   *(ULONG*)&sendBuffer[count] = htonl(numTimers);    count += 4;
1495
1496   for (int i = 0; i < numTimers; i++)
1497   {
1498     if (count > 49000) break;
1499
1500     timer = Timers.Get(i);
1501
1502 #if VDRVERSNUM < 10300
1503     *(ULONG*)&sendBuffer[count] = htonl(timer->Active());                 count += 4;
1504 #else
1505     *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive));       count += 4;
1506 #endif
1507     *(ULONG*)&sendBuffer[count] = htonl(timer->Recording());              count += 4;
1508     *(ULONG*)&sendBuffer[count] = htonl(timer->Pending());                count += 4;
1509     *(ULONG*)&sendBuffer[count] = htonl(timer->Priority());               count += 4;
1510     *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime());               count += 4;
1511     *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number());      count += 4;
1512     *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime());              count += 4;
1513     *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime());               count += 4;
1514
1515     fileName = timer->File();
1516     strcpy((char*)&sendBuffer[count], fileName);
1517     count += strlen(fileName) + 1;
1518   }
1519
1520   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1521
1522   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1523
1524   tcp.sendPacket(sendBuffer, count);
1525   delete[] sendBuffer;
1526   log->log("Client", Log::DEBUG, "Written timers list");
1527
1528   return 1;
1529 }
1530
1531 int MVPClient::processSetTimer(UCHAR* buffer, int length)
1532 {
1533   char* timerString = new char[strlen((char*)buffer) + 1];
1534   strcpy(timerString, (char*)buffer);
1535
1536 #if VDRVERSNUM < 10300
1537
1538   // If this is VDR 1.2 the date part of the timer string must be reduced
1539   // to just DD rather than YYYY-MM-DD
1540
1541   int s = 0; // source
1542   int d = 0; // destination
1543   int c = 0; // count
1544   while(c != 2) // copy up to date section, including the second ':'
1545   {
1546     timerString[d] = buffer[s];
1547     if (buffer[s] == ':') c++;
1548     ++s;
1549     ++d;
1550   }
1551   // now it has copied up to the date section
1552   c = 0;
1553   while(c != 2) // waste YYYY-MM-
1554   {
1555     if (buffer[s] == '-') c++;
1556     ++s;
1557   }
1558   // now source is at the DD
1559   memcpy(&timerString[d], &buffer[s], length - s);
1560   d += length - s;
1561   timerString[d] = '\0';
1562
1563   log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1564   log->log("Client", Log::DEBUG, "%s", timerString);
1565
1566 #endif
1567
1568   cTimer *timer = new cTimer;
1569   if (timer->Parse((char*)timerString))
1570   {
1571     cTimer *t = Timers.GetTimer(timer);
1572     if (!t)
1573     {
1574       Timers.Add(timer);
1575 #if VDRVERSNUM < 10300
1576       Timers.Save();
1577 #else
1578       Timers.SetModified();
1579 #endif
1580       sendULONG(0);
1581       return 1;
1582     }
1583     else
1584     {
1585       sendULONG(1);
1586     }
1587   }
1588   else
1589   {
1590      sendULONG(2);
1591   }
1592   delete timer;
1593   return 1;
1594 }
1595
1596 int MVPClient::processGetRecInfo(UCHAR* data, int length)
1597 {
1598   // data is a pointer to the fileName string
1599
1600   cRecordings Recordings;
1601   Recordings.Load(); // probably have to do this
1602
1603   cRecording *recording = Recordings.GetByName((char*)data);
1604
1605   time_t timerStart = 0;
1606   time_t timerStop = 0;
1607   char* summary = NULL;
1608   ULONG resumePoint = 0;
1609
1610   if (!recording)
1611   {
1612     log->log("Client", Log::ERR, "GetRecInfo found no recording");
1613     sendULONG(0);
1614     return 1;
1615   }
1616
1617   ULONG sendBufferSize = 10000;
1618   UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
1619   ULONG pos = 4; // leave first 4 bytes for size field
1620
1621
1622   /* Return packet:
1623   4 bytes: start time for timer
1624   4 bytes: end time for timer
1625   4 bytes: resume point
1626   string: summary
1627   4 bytes: num components
1628   {
1629     1 byte: stream
1630     1 byte: type
1631     string: language
1632     string: description
1633   }
1634
1635   */
1636
1637   // Get current timer
1638
1639   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1640   if (rc)
1641   {
1642     timerStart = rc->Timer()->StartTime();
1643     timerStop = rc->Timer()->StopTime();
1644     log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1645   }
1646
1647   *(time_t*)&sendBuffer[pos] = htonl(timerStart);    pos += 4;
1648   *(time_t*)&sendBuffer[pos] = htonl(timerStop);     pos += 4;
1649
1650   // Get resume point
1651
1652   char* value = config.getValueString("ResumeData", (char*)data);
1653   if (value)
1654   {
1655     resumePoint = strtoul(value, NULL, 10);
1656     delete[] value;
1657   }
1658   log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1659
1660   *(ULONG*)&sendBuffer[pos] = htonl(resumePoint);    pos += 4;
1661
1662
1663   // Get summary
1664
1665 #if VDRVERSNUM < 10300
1666   summary = (char*)recording->Summary();
1667 #else
1668   const cRecordingInfo *Info = recording->Info();
1669   summary = (char*)Info->ShortText();
1670   if (isempty(summary)) summary = (char*)Info->Description();
1671 #endif
1672   log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1673   if (summary)
1674   {
1675     // memory insanity...
1676     if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
1677     {
1678       UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
1679       if (newBuffer)
1680       {
1681         sendBuffer = newBuffer;
1682         sendBufferSize += strlen(summary) + 10000;
1683       }
1684       else
1685       {
1686         free(sendBuffer);
1687         sendULONG(0);
1688         return 1;
1689       }
1690     }
1691
1692     strcpy((char*)&sendBuffer[pos], summary);
1693     pos += strlen(summary) + 1;
1694   }
1695   else
1696   {
1697     strcpy((char*)&sendBuffer[pos], "");
1698     pos += 1;
1699   }
1700
1701
1702   // Get channels
1703
1704 #if VDRVERSNUM < 10300
1705
1706   // Send 0 for numchannels - this signals the client this info is not available
1707   *(ULONG*)&sendBuffer[pos] = 0;    pos += 4;
1708
1709 #else
1710   const cComponents* components = Info->Components();
1711
1712   log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1713
1714   if (!components)
1715   {
1716     *(ULONG*)&sendBuffer[pos] = htonl(0);    pos += 4;
1717   }
1718   else
1719   {
1720     *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents());    pos += 4;
1721
1722     tComponent* component;
1723     for (int i = 0; i < components->NumComponents(); i++)
1724     {
1725       component = components->Component(i);
1726
1727       // memory insanity...
1728       ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
1729                             + (component->description ? strlen(component->description) : 0) + 2;
1730
1731       if ((sendBufferSize - pos) < extraNeeded)
1732       {
1733         UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
1734         if (newBuffer)
1735         {
1736           sendBuffer = newBuffer;
1737           sendBufferSize += extraNeeded + 10000;
1738         }
1739         else
1740         {
1741           free(sendBuffer);
1742           sendULONG(0);
1743           return 1;
1744         }
1745       }
1746
1747       log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1748       sendBuffer[pos] = component->stream;  pos += 1;
1749       sendBuffer[pos] = component->type;    pos += 1;
1750       if (component->language)
1751       {
1752         strcpy((char*)&sendBuffer[pos], component->language);
1753         pos += strlen(component->language) + 1;
1754       }
1755       else
1756       {
1757         strcpy((char*)&sendBuffer[pos], "");
1758         pos += 1;
1759       }
1760       if (component->description)
1761       {
1762         strcpy((char*)&sendBuffer[pos], component->description);
1763         pos += strlen(component->description) + 1;
1764       }
1765       else
1766       {
1767         strcpy((char*)&sendBuffer[pos], "");
1768         pos += 1;
1769       }
1770
1771     }
1772   }
1773
1774 #endif
1775
1776   // Done. send it
1777
1778   *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 :  take off the size field
1779
1780   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1781
1782   tcp.sendPacket(sendBuffer, pos);
1783   delete[] sendBuffer;
1784   log->log("Client", Log::DEBUG, "Written getrecinfo");
1785
1786   return 1;
1787 }
1788
1789
1790
1791
1792 // FIXME obselete
1793
1794 int MVPClient::processReScanRecording(UCHAR* data, int length)
1795 {
1796   if (!rp)
1797   {
1798     log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1799     return 0;
1800   }
1801
1802   rp->scan();
1803
1804   UCHAR sendBuffer[16];
1805   *(ULONG*)&sendBuffer[0] = htonl(12);
1806   *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
1807   *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());
1808
1809   tcp.sendPacket(sendBuffer, 16);
1810   log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1811   return 1;
1812 }
1813
1814 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1815
1816
1817 int MVPClient::processGetMarks(UCHAR* data, int length)
1818 {
1819   // data is a pointer to the fileName string
1820
1821   UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
1822   int count = 4; // leave space for the packet length
1823
1824
1825   cMarks Marks;
1826   cRecordings Recordings;
1827   Recordings.Load(); // probably have to do this
1828
1829   cRecording *recording = Recordings.GetByName((char*)data);
1830
1831   log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1832
1833   if (recording)
1834   {
1835     Marks.Load(recording->FileName());
1836     if (Marks.Count())
1837     {
1838       for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1839       {
1840         log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1841
1842         if (count > 49000) break;
1843         *(ULONG*)&sendBuffer[count] = htonl(m->position);
1844         count += 4;
1845       }
1846     }
1847     else
1848     {
1849       log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1850       *(ULONG*)&sendBuffer[count] = htonl(0);
1851       count += 4;
1852     }
1853   }
1854
1855   *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field
1856
1857   log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));
1858
1859   tcp.sendPacket(sendBuffer, count);
1860   delete[] sendBuffer;
1861   log->log("Client", Log::DEBUG, "Written Marks list");
1862
1863   return 1;
1864 }