02_HTTPSRV.pm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #
  2. #
  3. # 02_HTTPSRV.pm
  4. # written by Dr. Boris Neubert 2012-08-27
  5. # e-mail: omega at online dot de
  6. #
  7. ##############################################
  8. # $Id: 02_HTTPSRV.pm 16874 2018-06-15 17:18:55Z neubert $
  9. package main;
  10. use strict;
  11. use warnings;
  12. use vars qw(%data);
  13. use HttpUtils;
  14. my $HTTPSRV_matchlink = "^\/?(([^\/]*(\/[^\/]+)*)\/?)\$";
  15. #########################
  16. sub
  17. HTTPSRV_addExtension($$$$) {
  18. my ($name,$func,$link,$friendlyname)= @_;
  19. # do some cleanup on link/url
  20. # link should really show the link as expected to be called (might include trailing / but no leading /)
  21. # url should only contain the directory piece with a leading / but no trailing /
  22. # $1 is complete link without potentially leading /
  23. # $2 is complete link without potentially leading / and trailing /
  24. $link =~ /$HTTPSRV_matchlink/;
  25. my $url = "/".$2;
  26. my $modlink = $1;
  27. Log3 $name, 3, "Registering HTTPSRV $name for URL $url and assigned link $modlink ...";
  28. $data{FWEXT}{$url}{deviceName}= $name;
  29. $data{FWEXT}{$url}{FUNC} = $func;
  30. $data{FWEXT}{$url}{LINK} = $modlink;
  31. $data{FWEXT}{$url}{NAME} = $friendlyname;
  32. }
  33. sub
  34. HTTPSRV_removeExtension($) {
  35. my ($link)= @_;
  36. # do some cleanup on link/url
  37. # link should really show the link as expected to be called (might include trailing / but no leading /)
  38. # url should only contain the directory piece with a leading / but no trailing /
  39. # $1 is complete link without potentially leading /
  40. # $2 is complete link without potentially leading / and trailing /
  41. $link =~ /$HTTPSRV_matchlink/;
  42. my $url = "/".$2;
  43. my $name= $data{FWEXT}{$url}{deviceName};
  44. Log3 $name, 3, "Unregistering HTTPSRV $name for URL $url...";
  45. delete $data{FWEXT}{$url};
  46. }
  47. ##################
  48. sub
  49. HTTPSRV_Initialize($) {
  50. my ($hash) = @_;
  51. $hash->{DefFn} = "HTTPSRV_Define";
  52. $hash->{DefFn} = "HTTPSRV_Define";
  53. $hash->{UndefFn} = "HTTPSRV_Undef";
  54. #$hash->{AttrFn} = "HTTPSRV_Attr";
  55. $hash->{AttrList} = "directoryindex " .
  56. "readings";
  57. $hash->{AttrFn} = "HTTPSRV_Attr";
  58. #$hash->{SetFn} = "HTTPSRV_Set";
  59. return undef;
  60. }
  61. ##################
  62. sub
  63. HTTPSRV_Define($$) {
  64. my ($hash, $def) = @_;
  65. my @a = split("[ \t]+", $def, 5);
  66. return "Usage: define <name> HTTPSRV <infix> <directory> <friendlyname>" if(int(@a) != 5);
  67. my $name= $a[0];
  68. my $infix= $a[2];
  69. my $directory= $a[3];
  70. my $friendlyname= $a[4];
  71. $hash->{fhem}{infix}= $infix;
  72. $hash->{fhem}{directory}= $directory;
  73. $hash->{fhem}{friendlyname}= $friendlyname;
  74. Log3 $name, 3, "$name: new ext defined infix:$infix: dir:$directory:";
  75. HTTPSRV_addExtension($name, "HTTPSRV_CGI", $infix, $friendlyname);
  76. $hash->{STATE} = $name;
  77. return undef;
  78. }
  79. ##################
  80. sub
  81. HTTPSRV_Undef($$) {
  82. my ($hash, $name) = @_;
  83. HTTPSRV_removeExtension($hash->{fhem}{infix});
  84. return undef;
  85. }
  86. ##################
  87. sub
  88. HTTPSRV_Attr(@)
  89. {
  90. my ($cmd,$name,$aName,$aVal) = @_;
  91. if ($cmd eq "set") {
  92. if ($aName =~ "readings") {
  93. if ($aVal !~ /^[A-Z_a-z0-9\,]+$/) {
  94. Log3 $name, 3, "$name: Invalid reading list in attr $name $aName $aVal (only A-Z, a-z, 0-9, _ and , allowed)";
  95. return "Invalid reading name $aVal (only A-Z, a-z, 0-9, _ and , allowed)";
  96. }
  97. addToDevAttrList($name, $aName);
  98. }
  99. }
  100. return undef;
  101. }
  102. ##################
  103. #
  104. # here we answer any request to http://host:port/fhem/$infix and below
  105. sub HTTPSRV_CGI() {
  106. my ($request) = @_; # /$infix/filename
  107. # Debug "request= $request";
  108. # Match request first without trailing / in the link part
  109. if($request =~ m,^(/[^/]+)(/(.*)?)?$,) {
  110. my $link= $1;
  111. my $filename= $3;
  112. my $name;
  113. # If FWEXT not found for this make a second try with a trailing slash in the link part
  114. if(! $data{FWEXT}{$link}) {
  115. $link = $link."/";
  116. return("text/plain; charset=utf-8", "Illegal request: $request") if(! $data{FWEXT}{$link});
  117. }
  118. # get device name
  119. $name= $data{FWEXT}{$link}{deviceName};
  120. # Debug "link= $link";
  121. # Debug "filename= $filename";
  122. # Debug "name= $name";
  123. # return error if no such device
  124. return("text/plain; charset=utf-8", "No HTTPSRV device for $link") unless($name);
  125. my $fullName = $filename;
  126. foreach my $reading (split (/,/, AttrVal($name, "readings", ""))) {
  127. my $value = "";
  128. if ($fullName =~ /^([^\?]+)\?(.*)($reading)=([^;&]*)([&;].*)?$/) {
  129. $filename = $1;
  130. $value = $4;
  131. Log3 $name, 5, "$name: set Reading $reading = $value";
  132. readingsSingleUpdate($defs{$name}, $reading, $value, 1);
  133. }
  134. };
  135. # set directory index
  136. $filename= AttrVal($name,"directoryindex","index.html") unless($filename);
  137. $filename =~ s/\?.*//;
  138. my $MIMEtype= filename2MIMEType($filename);
  139. my $directory= $defs{$name}{fhem}{directory};
  140. $filename= "$directory/$filename";
  141. #Debug "read filename= $filename";
  142. my @contents;
  143. if(open(INPUTFILE, $filename)) {
  144. binmode(INPUTFILE);
  145. @contents= <INPUTFILE>;
  146. close(INPUTFILE);
  147. return("$MIMEtype; charset=utf-8", join("", @contents));
  148. } else {
  149. return("text/plain; charset=utf-8", "File not found: $filename");
  150. }
  151. } else {
  152. return("text/plain; charset=utf-8", "Illegal request: $request");
  153. }
  154. }
  155. ####
  156. 1;
  157. =pod
  158. =item device
  159. =item summary rudimentary HTTP server that accepts parameters as readings
  160. =item summary_DE rudiment&auml;rer HTTP-Server, der auch Parameter als Readings akzeptiert
  161. =begin html
  162. <a name="HTTPSRV"></a>
  163. <h3>HTTPSRV</h3>
  164. <ul>
  165. Provides a mini HTTP server plugin for FHEMWEB. It serves files from a given directory.
  166. It optionally accepts a query string to set readings of this device if an attribute allows the given reading<p>
  167. HTTPSRV is an extension to <a href="HTTPSRV">FHEMWEB</a>. You must install FHEMWEB to use HTTPSRV.</p>
  168. <a name="HTTPSRVdefine"></a>
  169. <b>Define</b>
  170. <ul>
  171. <code>define &lt;name&gt; HTTPSRV &lt;infix&gt; &lt;directory&gt; &lt;friendlyname&gt;</code><br><br>
  172. Defines the HTTP server. <code>&lt;infix&gt;</code> is the portion behind the FHEMWEB base URL (usually
  173. <code>http://hostname:8083/fhem</code>), <code>&lt;directory&gt;</code> is the absolute path the
  174. files are served from, and <code>&lt;friendlyname&gt;</code> is the name displayed in the side menu of FHEMWEB.<p><p>
  175. Example:
  176. <ul>
  177. <code>define myJSFrontend HTTPSRV jsf /usr/share/jsfrontend My little frontend</code><br>
  178. or <br>
  179. <code>
  180. define kindleweb HTTPSRV kindle /opt/fhem/kindle Kindle Web<br>
  181. attr kindleweb readings KindleBatt
  182. </code><br>
  183. </ul>
  184. <br>
  185. </ul>
  186. <a name="HTTPSRVset"></a>
  187. <b>Set</b>
  188. <ul>
  189. n/a
  190. </ul>
  191. <br><br>
  192. <a name="HTTPSRVattr"></a>
  193. <b>Attributes</b>
  194. <br><br>
  195. <ul>
  196. <li>directoryindex: if the request is sent with no filename, i.e. the infix (with or without trailing slash) only, the file given in this attribute is loaded. Defaults to <code>index.html</code>.</li>
  197. <li>readings: a comma separated list of reading names. If the request ends with a querystring like <code>?Batt=43</code> and an attribute is set like <code>attr kindleweb readings Batt</code>, then a reading with the Name of this Attribute (here Batt) is created with the value from the request.</li>
  198. </ul>
  199. <br><br>
  200. <b>Usage information</b>
  201. <br><br>
  202. <ul>
  203. The above example on <code>http://hostname:8083/fhem</code> will return the file
  204. <code>/usr/share/jsfrontend/foo.html</code> for <code>http://hostname:8083/fhem/jsf/foo.html</code>.
  205. If no filename is given, the filename prescribed by the <code>directoryindex</code> attribute is returned.<p>
  206. Notice: All links are relative to <code>http://hostname:8083/fhem</code>.
  207. </ul>
  208. <br><br>
  209. </ul>
  210. =end html
  211. =cut