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