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