]> git.vomp.tv Git - jsonserver.git/blob - handler.c
Server returns path of recording after move
[jsonserver.git] / handler.c
1 #include "handler.h"
2
3 #include <string.h>
4 #include <stdlib.h>
5 #include <jsoncpp/json/json.h>
6 #include <string>
7
8 #include <vdr/videodir.h>
9 #include <vdr/recording.h>
10 #include <vdr/menu.h>
11 #include <vdr/timers.h>
12
13 #include "log.h"
14
15 int jsonserver_request_handler(struct mg_connection *conn)
16 {
17   Log* log = Log::getInstance();
18   const struct mg_request_info *request_info = mg_get_request_info(conn);
19   
20   if (strcmp(request_info->uri, "/jsonserver")) return 0; // not for us
21
22   char wvrequest[20];
23   int wvrl = mg_get_var(request_info->query_string, strlen(request_info->query_string), "req", wvrequest, 20);
24   if (wvrl == -1)
25   {
26     log->log("JSONServer", Log::ERR, "Could not decode req");
27     return 0;
28   }
29
30 /*
31   if (!strcmp(request_info->request_method, "OPTIONS"))
32   {
33     mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n");
34     mg_printf(conn, "%s", "Access-Control-Allow-Origin: *\r\n");
35     mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
36     return (void*)1;
37   }
38 */
39
40   // Get POST data
41   char postData[10000];
42   postData[0] = '\0';
43   if (!strcmp(request_info->request_method, "POST"))
44   {
45     const char* contentLength = mg_get_header(conn, "Content-Length");
46     int contentLengthI = atoi(contentLength);
47     log->log("JSONServer", Log::DEBUG, "POST data content length: %i", contentLengthI);
48     if (contentLengthI > 10000)
49     {
50       log->log("JSONServer", Log::DEBUG, "Length > 10000, rejecting");
51       return 0;
52     }
53
54     if (contentLengthI > 0)
55     {
56       // FIXME - assume for now that all post data will be small enough to have arrived immediately
57       int bytesRead = mg_read(conn, postData, contentLengthI);
58       if (bytesRead != contentLengthI)
59       {
60         log->log("JSONServer", Log::DEBUG, "Could not read up to contentLength");
61         return 0;
62       }
63       postData[contentLengthI] = '\0';
64     }
65   }
66
67   Json::Value js;
68   bool success = false;
69
70   if      (!strcmp(wvrequest, "reclist")) success = jsonserver_reclist(js);
71   else if (!strcmp(wvrequest, "recinfo")) success = jsonserver_recinfo(js, postData);
72   else if (!strcmp(wvrequest, "recdel")) success = jsonserver_recdel(js, postData);
73   else if (!strcmp(wvrequest, "recmove")) success = jsonserver_recmove(js, postData);
74   else if (!strcmp(wvrequest, "channellist")) success = jsonserver_channellist(js);
75   else if (!strcmp(wvrequest, "channelschedule")) success = jsonserver_channelschedule(js, postData);
76   else if (!strcmp(wvrequest, "timerlist")) success = jsonserver_timerlist(js);
77   else if (!strcmp(wvrequest, "timerdel")) success = jsonserver_timerdel(js, postData);
78   else if (!strcmp(wvrequest, "timerset")) success = jsonserver_timerset(js, postData);
79
80   if (!success) return 0; // the specific handler failed badly
81
82   // Now js will be filled
83
84   Json::StyledWriter sw;
85   std::string jsonout = sw.write(js);
86   mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n");
87   mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
88   mg_write(conn, jsonout.c_str(), jsonout.length());
89   return 1;
90
91   
92 /*
93   else if (event == MG_EVENT_LOG)
94   {
95     if (request_info->status_code == 400) // bad request
96     {
97       log->log("Mongoose", Log::DEBUG, "400 BAD REQUEST:");
98       log->log("Mongoose", Log::DEBUG, request_info->request_method);
99       log->log("Mongoose", Log::DEBUG, request_info->uri);
100       log->log("Mongoose", Log::DEBUG, request_info->http_version);
101       log->log("Mongoose", Log::DEBUG, request_info->query_string);
102       log->log("Mongoose", Log::DEBUG, request_info->log_message);
103       for (int i = 0; i < request_info->num_headers; i++)
104       {
105         log->log("Mongoose", Log::DEBUG, "%s: %s", request_info->http_headers[i].name, request_info->http_headers[i].value);
106       }
107     }
108     else
109     {
110       log->log("Mongoose", Log::DEBUG, request_info->log_message);
111       log->log("Mongoose", Log::DEBUG, request_info->uri);
112     }
113     return (void*)1;
114   }
115   
116   // other events not handled:
117   // MG_HTTP_ERROR, MG_INIT_SSL
118   // Let mongoose do something with those
119
120   return NULL;
121
122 */
123   
124 }
125
126 bool jsonserver_reclist(Json::Value& js)
127 {
128   Log* log = Log::getInstance();
129   log->log("JSONServer", Log::DEBUG, "reclist");
130
131   int FreeMB;
132   int Percent = VideoDiskSpace(&FreeMB);
133   int Total = (FreeMB / (100 - Percent)) * 100;
134         
135   js["MBFree"] = FreeMB;
136   js["Percent"] = Percent;
137   js["Total"] = Total;
138   
139   Json::Value jsrecordings;
140   cRecordings Recordings;
141   Recordings.Load();
142   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
143   {
144     Json::Value oneRec;
145     oneRec["StartTime"] = (Json::UInt)recording->Start();
146     oneRec["Length"] = (Json::UInt)recording->LengthInSeconds();
147     oneRec["IsNew"] = recording->IsNew();
148     oneRec["Name"] = recording->Name();
149     oneRec["Filename"] = recording->FileName();
150     jsrecordings.append(oneRec);
151   }
152   js["Recordings"] = jsrecordings;
153   
154   return true;
155 }
156
157 bool jsonserver_recinfo(Json::Value& js, const char* postData)
158 {
159   Log* log = Log::getInstance();
160   log->log("JSONServer", Log::DEBUG, "recinfo");
161
162   char reqfilename[1000];
163   int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
164   if (mgv1 == -1)
165   {
166     log->log("JSONServer", Log::ERR, "Could not decode filename");
167     js["Result"] = false;
168     js["Error"] = "Could not decode filename";
169     return true;
170   }
171
172   log->log("JSONServer", Log::DEBUG, "%s", reqfilename);
173   
174   cRecordings Recordings;
175   Recordings.Load(); // probably have to do this
176   cRecording *recording = Recordings.GetByName(reqfilename);
177
178   if (!recording)
179   {
180     log->log("JSONServer", Log::ERR, "recinfo found no recording");
181     js["Result"] = false;
182     return true;
183   }
184
185   js["IsNew"] = recording->IsNew();
186   js["LengthInSeconds"] = recording->LengthInSeconds();
187   js["FileSizeMB"] = recording->FileSizeMB();
188   js["Name"] = recording->Name() ? recording->Name() : Json::Value::null;
189   js["Priority"] = recording->Priority();
190   js["LifeTime"] = recording->Lifetime();
191   js["Start"] = (Json::UInt)recording->Start();
192
193   js["ResumePoint"] = 0;
194   cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
195   if (ResumeFile.Read() >= 0) js["ResumePoint"] = ResumeFile.Read();
196
197   js["CurrentlyRecordingStart"] = 0;
198   js["CurrentlyRecordingStop"] = 0;
199   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
200   if (rc)
201   {
202     js["CurrentlyRecordingStart"] = (Json::UInt)rc->Timer()->StartTime();
203     js["CurrentlyRecordingStop"] = (Json::UInt)rc->Timer()->StopTime();
204   }
205
206   const cRecordingInfo *info = recording->Info();
207   if (info)
208   {
209     js["ChannelName"] = info->ChannelName() ? info->ChannelName() : Json::Value::null;
210     js["Title"] = info->Title() ? info->Title() : Json::Value::null;
211     js["ShortText"] = info->ShortText() ? info->ShortText() : Json::Value::null;
212     js["Description"] = info->Description() ? info->Description() : Json::Value::null;
213
214     const cComponents* components = info->Components();
215     if (!components)
216     {
217       js["Components"] = Json::Value::null;
218     }
219     else
220     {
221       Json::Value jscomponents;
222
223       tComponent* component;
224       for (int i = 0; i < components->NumComponents(); i++)
225       {
226         component = components->Component(i);
227
228         Json::Value oneComponent;
229         oneComponent["Stream"] = component->stream;
230         oneComponent["Type"] = component->type;
231         oneComponent["Language"] = component->language ? component->language : Json::Value::null;
232         oneComponent["Description"] = component->description ? component->description : Json::Value::null;
233         jscomponents.append(oneComponent);
234       }
235
236       js["Components"] = jscomponents;
237     }
238   }
239
240   js["Result"] = true;
241   return true;
242 }
243
244 bool jsonserver_recdel(Json::Value& js, const char* postData)
245 {
246   Log* log = Log::getInstance();
247   log->log("JSONServer", Log::DEBUG, "recdel");
248
249   char reqfilename[1000];
250   int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
251   if (mgv1 == -1)
252   {
253     log->log("JSONServer", Log::ERR, "Could not decode filename");
254     js["Result"] = false;
255     js["Error"] = "Could not decode filename";
256     return true;
257   }
258
259   log->log("JSONServer", Log::DEBUG, "%s", reqfilename);
260   
261   cRecordings Recordings;
262   Recordings.Load(); // probably have to do this
263   cRecording *recording = Recordings.GetByName(reqfilename);
264
265   if (!recording)
266   {
267     js["Result"] = false;
268     js["Error"] = "Could not find recording to delete";
269     return true;
270   }
271
272   log->log("JSONServer", Log::DEBUG, "Deleting recording: %s", recording->Name());
273   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
274   if (rc)
275   {
276     js["Result"] = false;
277     js["Error"] = "This recording is still recording.. ho ho";
278     return true;
279   }
280
281   if (recording->Delete())
282   {
283     ::Recordings.DelByName(recording->FileName());
284     js["Result"] = true;
285   }
286   else
287   {
288     js["Result"] = false;
289     js["Error"] = "Failed to delete recording";
290   }
291
292   return true;
293 }
294
295 bool jsonserver_recmove(Json::Value& js, const char* postData)
296 {
297   Log* log = Log::getInstance();
298   log->log("JSONServer", Log::DEBUG, "recmove");
299
300   char sFileName[1000]; int mgv1 = mg_get_var(postData, strlen(postData), "filename", sFileName, 1000);  
301   char sNewPath[1000];  int mgv2 = mg_get_var(postData, strlen(postData), "newpath", sNewPath, 1000);  
302
303   if ( (mgv1 == -1) || (mgv2 == -1) || !strlen(sFileName) || !strlen(sNewPath) )
304   {
305     log->log("JSONServer", Log::ERR, "request mgvs: %i %i", mgv1, mgv2);
306     js["Result"] = false;
307     js["Error"] = "Bad request parameters";
308     return true;
309   }
310
311   cRecordings Recordings;
312   Recordings.Load(); // probably have to do this
313   cRecording* recording = Recordings.GetByName(sFileName);
314
315   if (!recording)
316   {
317     log->log("JSONServer", Log::ERR, "Could not find recording to move");
318     js["Result"] = false;
319     js["Error"] = "Bad filename";
320     return true;
321   }
322
323   cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
324   if (rc)
325   {
326     log->log("JSONServer", Log::ERR, "Could not move recording, it is still recording");
327     js["Result"] = false;
328     js["Error"] = "Cannot move recording in progress";
329     return true;
330   }
331   
332   log->log("JSONServer", Log::DEBUG, "moving recording: %s", recording->Name());
333   log->log("JSONServer", Log::DEBUG, "moving recording: %s", recording->FileName());
334   log->log("JSONServer", Log::DEBUG, "to: %s", sNewPath);
335
336   const char* t = recording->FileName();
337
338   char* dateDirName = NULL;   int k;
339   char* titleDirName = NULL;  int j;
340   char* newContainer = NULL;
341
342   // Find the datedirname
343   for(k = strlen(t) - 1; k >= 0; k--)
344   {
345     if (t[k] == '/')
346     {
347       log->log("JSONServer", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
348       dateDirName = new char[strlen(&t[k+1]) + 1];
349       strcpy(dateDirName, &t[k+1]);
350       break;
351     }
352   }
353
354   // Find the titledirname
355
356   for(j = k-1; j >= 0; j--)
357   {
358     if (t[j] == '/')
359     {
360       log->log("JSONServer", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
361       titleDirName = new char[(k - j - 1) + 1];
362       memcpy(titleDirName, &t[j+1], k - j - 1);
363       titleDirName[k - j - 1] = '\0';
364       break;
365     }
366   }
367
368   log->log("JSONServer", Log::DEBUG, "datedirname: %s", dateDirName);
369   log->log("JSONServer", Log::DEBUG, "titledirname: %s", titleDirName);
370   log->log("JSONServer", Log::DEBUG, "viddir: %s", VideoDirectory);
371
372   ExchangeChars(sNewPath, true);
373   log->log("JSONServer", Log::DEBUG, "EC: %s", sNewPath);
374
375   newContainer = new char[strlen(VideoDirectory) + strlen(sNewPath) + strlen(titleDirName) + 1];
376   log->log("JSONServer", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(sNewPath) + strlen(titleDirName) + 1);
377   sprintf(newContainer, "%s%s%s", VideoDirectory, sNewPath, titleDirName);
378   log->log("JSONServer", Log::DEBUG, "%s", newContainer);
379
380   struct stat dstat;
381   int statret = stat(newContainer, &dstat);
382   if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
383   {
384     log->log("JSONServer", Log::DEBUG, "new dir does not exist");
385     int mkdirret = mkdir(newContainer, 0755);
386     if (mkdirret != 0)
387     {
388       delete[] dateDirName;
389       delete[] titleDirName;
390       delete[] newContainer;
391
392       log->log("JSONServer", Log::ERR, "Failed to make new dir");
393       js["Result"] = false;
394       js["Error"] = "Failed to create new directory";
395       return true;
396     }
397   }
398   else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
399   {
400     delete[] dateDirName;
401     delete[] titleDirName;
402     delete[] newContainer;
403
404     log->log("JSONServer", Log::ERR, "Something already exists?");
405     js["Result"] = false;
406     js["Error"] = "Something already exists at the new path";
407     return true;
408   }
409
410   // Ok, the directory container has been made, or it pre-existed.
411
412   char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
413   sprintf(newDir, "%s/%s", newContainer, dateDirName);
414
415   log->log("JSONServer", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
416   int renameret = rename(t, newDir);
417   if (renameret == 0)
418   {
419     // Success. Test for remove old dir containter
420     char* oldTitleDir = new char[k+1];
421     memcpy(oldTitleDir, t, k);
422     oldTitleDir[k] = '\0';
423     log->log("JSONServer", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
424     rmdir(oldTitleDir); // can't do anything about a fail result at this point.
425     delete[] oldTitleDir;
426
427     ::Recordings.Update();
428     js["Result"] = true;
429     js["NewRecordingFileName"] = newDir;
430   }
431   else
432   {
433     log->log("JSONServer", Log::ERR, "Rename failed");
434     js["Result"] = false;
435     js["Error"] = "Move failed";
436   }
437
438   delete[] dateDirName;
439   delete[] titleDirName;
440   delete[] newContainer;
441   delete[] newDir;
442
443   return true;
444 }
445
446 bool jsonserver_channellist(Json::Value& js)
447 {
448   Log* log = Log::getInstance();
449   log->log("JSONServer", Log::DEBUG, "channellist");
450
451   Json::Value jschannels;
452   
453   int type;
454   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
455   {
456     if (!channel->GroupSep())
457     {
458       log->log("JSONServer", Log::DEBUG, "name: '%s'", channel->Name());
459
460       if (channel->Vpid()) type = 1;
461       else if (channel->Apid(0)) type = 2;
462       else continue;
463
464       Json::Value oneChannel;
465       oneChannel["Number"] = channel->Number();
466       oneChannel["Type"] = type;
467       oneChannel["Name"] = channel->Name();
468 #if VDRVERSNUM < 10703
469       oneChannel["VType"] = 2;
470 #else
471       oneChannel["VType"] = channel->Vtype();
472 #endif
473       jschannels.append(oneChannel);    
474     }
475   }
476   js["Channels"] = jschannels;
477   
478   return true;
479 }
480
481 bool jsonserver_channelschedule(Json::Value& js, const char* postData)
482 {
483   Log* log = Log::getInstance();
484   log->log("JSONServer", Log::DEBUG, "channelschedule '%s'", postData);
485
486   char sChannelNumber[15];  int mgv1 = mg_get_var(postData, strlen(postData), "channelnumber", sChannelNumber, 15);  
487   char sStartTime[15];      int mgv2 = mg_get_var(postData, strlen(postData), "starttime", sStartTime, 15);  
488   char sDuration[15];       int mgv3 = mg_get_var(postData, strlen(postData), "duration", sDuration, 15);  
489
490   if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) )
491   {
492     log->log("JSONServer", Log::ERR, "request mgvs: %i %i %i", mgv1, mgv2, mgv3);
493     js["Result"] = false;
494     js["Error"] = "Bad request parameters";
495     return true;
496   }
497
498   int channelNumber = atoi(sChannelNumber);
499   int startTime = atoi(sStartTime); 
500   int duration = atoi(sDuration); 
501
502   cChannel* channel = NULL;
503   for (channel = Channels.First(); channel; channel = Channels.Next(channel))
504   {
505     if (channel->GroupSep()) continue;
506     if (channel->Number() == channelNumber) break;
507   }
508
509   if (!channel)
510   {
511     log->log("JSONServer", Log::ERR, "Could not find requested channel: %i", channelNumber);
512     js["Result"] = false;
513     js["Error"] = "Could not find channel";
514     return true;
515   }
516
517   cSchedulesLock MutexLock;
518   const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
519   if (!Schedules)
520   {
521     log->log("JSONServer", Log::ERR, "Could not find requested channel: %i", channelNumber);
522     js["Result"] = false;
523     js["Error"] = "Internal schedules error (1)";
524     return true;
525   }
526   const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
527   if (!Schedule)     
528   {
529     log->log("JSONServer", Log::ERR, "Could not find requested channel: %i", channelNumber);
530     js["Result"] = false;
531     js["Error"] = "Internal schedules error (2)";
532     return true;
533   }
534     
535   Json::Value jsevents;
536
537   for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
538   {
539     //in the past filter
540     if ((event->StartTime() + event->Duration()) < time(NULL)) continue;
541    //start time filter
542     if ((event->StartTime() + event->Duration()) <= startTime) continue;
543     //duration filter
544     if (event->StartTime() >= (startTime + duration)) continue;
545
546     Json::Value oneEvent;
547     oneEvent["ID"] = event->EventID();
548     oneEvent["Time"] = (Json::UInt)event->StartTime();
549     oneEvent["Duration"] = event->Duration();
550     oneEvent["Title"] = event->Title() ? event->Title() : "";
551     oneEvent["Description"] = event->Description() ? event->Description() : "";
552     jsevents.append(oneEvent);
553   }
554
555   js["Events"] = jsevents;
556   return true;
557 }
558
559 bool jsonserver_timerlist(Json::Value& js)
560 {
561   Log* log = Log::getInstance();
562   log->log("JSONServer", Log::DEBUG, "timerlist");
563
564   Json::Value jstimers;
565   
566   cTimer *timer;
567   int numTimers = Timers.Count();
568
569   for (int i = 0; i < numTimers; i++)
570   {
571     timer = Timers.Get(i);
572     Json::Value oneTimer;
573     oneTimer["Active"] = timer->HasFlags(tfActive);
574     oneTimer["Recording"] = timer->Recording();
575     oneTimer["Pending"] = timer->Pending();
576     oneTimer["Priority"] = timer->Priority();
577     oneTimer["Lifetime"] = timer->Lifetime();
578     oneTimer["ChannelNumber"] = timer->Channel()->Number();
579     oneTimer["StartTime"] = (int)timer->StartTime();
580     oneTimer["StopTime"] = (int)timer->StopTime();
581     oneTimer["Day"] = (int)timer->Day();
582     oneTimer["WeekDays"] = timer->WeekDays();
583     oneTimer["Name"] = timer->File();
584     jstimers.append(oneTimer);
585   }
586   
587   js["Timers"] = jstimers;
588   return true;
589 }
590
591 bool jsonserver_timerdel(Json::Value& js, const char* postData)
592 {
593   Log* log = Log::getInstance();
594   log->log("JSONServer", Log::DEBUG, "timerdel");
595
596   char sdelChannel[15];   int mgv1 = mg_get_var(postData, strlen(postData), "delchannel", sdelChannel, 15);  
597   char sdelWeekdays[15];  int mgv2 = mg_get_var(postData, strlen(postData), "delweekdays", sdelWeekdays, 15);  
598   char sdelDay[15];       int mgv3 = mg_get_var(postData, strlen(postData), "delday", sdelDay, 15);  
599   char sdelStart[15];     int mgv4 = mg_get_var(postData, strlen(postData), "delstart", sdelStart, 15);  
600   char sdelStop[15];      int mgv5 = mg_get_var(postData, strlen(postData), "delstop", sdelStop, 15);  
601
602   if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) || (mgv4 == -1) || (mgv5 == -1) )
603   {
604     log->log("JSONServer", Log::ERR, "request mgvs: %i %i %i %i %i", mgv1, mgv2, mgv3, mgv4, mgv5);
605     js["Result"] = false;
606     js["Error"] = "Bad request parameters";
607     return true;
608   }
609
610   int delChannel = atoi(sdelChannel); 
611   int delWeekdays = atoi(sdelWeekdays); 
612   int delDay = atoi(sdelDay); 
613   int delStart = atoi(sdelStart); 
614   int delStop = atoi(sdelStop); 
615
616   cTimer* ti = NULL;
617   for (ti = Timers.First(); ti; ti = Timers.Next(ti))
618   {
619     if  ( (ti->Channel()->Number() == delChannel)
620      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
621      &&   (ti->StartTime() == delStart)
622      &&   (ti->StopTime() == delStop) )
623        break;
624   }
625   
626   if (!ti)
627   {
628     log->log("JSONServer", Log::ERR, "Could not find timer");
629     js["Result"] = false;
630     js["Error"] = "Could not find timer";
631     return true;
632   }
633
634   if (Timers.BeingEdited())
635   {
636     log->log("JSONServer", Log::ERR, "Unable to delete timer - timers being edited at VDR");
637     js["Result"] = false;
638     js["Error"] = "Timers being edited at VDR";
639     return true; 
640   }
641   
642   if (ti->Recording())
643   {
644     log->log("JSONServer", Log::ERR, "Unable to delete timer - timer is running");
645     js["Result"] = false;
646     js["Error"] = "Timer is running";
647     return true;
648   }
649   
650   Timers.Del(ti);
651   Timers.SetModified();
652   js["Result"] = true;
653     
654   return true;
655 }
656
657 bool jsonserver_timerset(Json::Value& js, const char* postData)
658 {
659   Log* log = Log::getInstance();
660   log->log("JSONServer", Log::DEBUG, "timerset");
661
662   char sTimerString[1024];   int mgv1 = mg_get_var(postData, strlen(postData), "timerstring", sTimerString, 1024);  
663
664   if (mgv1 == -1)
665   {
666     log->log("JSONServer", Log::ERR, "Could not get timerstring");
667     js["Result"] = false;
668     js["Error"] = "Bad request parameters";
669     return true;
670   }
671
672   log->log("JSONServer", Log::DEBUG, "'%s'", sTimerString);
673   cTimer *timer = new cTimer;
674   if (!timer->Parse(sTimerString))
675   {
676     delete timer;
677     js["Result"] = false;
678     js["Error"] = "Failed to parse timer request details";
679     return true;
680   }
681
682   cTimer *t = Timers.GetTimer(timer);
683   if (t)
684   {
685     delete timer;
686     js["Result"] = false;
687     js["Error"] = "Timer already exists";
688     return true;
689   }
690
691   Timers.Add(timer);
692   Timers.SetModified();
693   js["Result"] = true;
694   return true;
695 }
696