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