/************************************************************************************
** myinterw.c   cgi interface wartung
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/shm.h>    //Used for shared memory
#include <sys/ipc.h>
#include "rxdcb.h"

typedef struct
{
   unsigned char ss;            // Sekunden
   unsigned char mm;            // Minute
   unsigned char hh;            // Stunde
   unsigned char day;           // Tag
   unsigned char wday;          // Wochentag
   unsigned char mon;           // Monat
   unsigned char year;          // Jahr
   unsigned char dmz;           // MEZ oder MESZ
   unsigned char message_flag;  // time message überwachung
} S_CLOCK;

S_CLOCK s_clock;                // Zeit gmess

// ------------------------------- globale variablen ----
extern int errno;

// *************************************** shared memory Basis ***************
int shared_memory1_id;
S_BASIS *a_basis;                  // reiner Datenteil

int shared_memory2_id;
char *tdata;                       // Temperatur Werte für 2 Tage

int shared_memory3_id;
unsigned char *regenwerte;         // Regen Werte

int shared_memory4_id;
unsigned char *counter;            // Heizung Strom und Gas

int shared_memory5_id;
S_RDWR *a_rdwr;                    // communication area

char cnt_text[20];
char monattext[20];
char query[100];
char *qx[10];
int qanz = 0;

                       // 1   2   3   4   5   6   7   8   9   10  11  12
unsigned char motab[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int ff_read;
int ff_readcount;

MD5Context md5ctx[ 1 ] ;
unsigned char code[18] = "01234567Key477349";

// ------------------------------- Prototypen -----------
void print_header( int jssw, char *title );
void print_navi( void );
void print_footer(void);
void dumpcounter(void);
void writecounter( void );
void dumpregen(void);
void writeregen( void );
void dumpcelsius(void);
void clearcelsius(void);
void errorseite( void );
void testseite( void );
void updatesharedm(void);

unsigned int a_to_i( char *quelle, int anzahl);
int krypt_password(void);

int shared_varxx(void);
int shared_temperatur(void);
int shared_regen(void);
int shared_counter(void);
int shared_rxdcb(void);

void  textfifo_startread(S_FIFO_TEXT *ff);
char* textfifo_get(S_FIFO_TEXT *ff);

void MD5Init     ( MD5Context *context);
void MD5Update   ( MD5Context *context, unsigned char *buf, unsigned long len);
void MD5Final    ( unsigned char digest[16], MD5Context *context);
static void MD5Transform(  unsigned long buf[4], unsigned long const in[16]);
static void byteReverse (  unsigned char *buf, unsigned long longs);

/************************************************************************************
** Main
*/
int main(int argc, char *argv[])
{
   int pos,len,i;
   char *p_query ;             // query string
   char *p_req_method;         // request Method
   char rdummy[]="GET";
   int auswahl=0;
   char *sx;

   for (i=0; i<10; i++)        // clear pointer
      qx[i] = NULL;


   p_query = getenv( "QUERY_STRING" );
   if ( p_query == NULL )
       query[0] = '\0';
   else
      strcpy( query, p_query );
   len = strlen(query);
   if ( len > 0 )
   {
      qanz = 1;
      qx[0] = query;

      for( pos = 0; pos < len; pos++)
      {
         if ( query[pos] == '&' )
         {
            query[pos] = '\0';
            qx[qanz++] = query+pos+1;
         }
      }
   }

   p_req_method = getenv( "REQUEST_METHOD" );
   if ( p_req_method == NULL )
      p_req_method = rdummy;

   if ( strncmp( p_req_method, "GET", 3) == 0 )
   {
      if ( qanz == 0 )
         auswahl=8;         // Testseite
      else
      {
         for ( i=0; i < qanz; i++)             // loop durch die query
         {
            sx = qx[i];
            switch ( sx[0] )
            {
              case 'C':    // Wartung Counter
               auswahl=1;
               if ( strlen(sx) > 2 &&  sx[2] == '2' )
                  auswahl=2;
               break;

              case 'N':    // Wartung Niederschlag
               auswahl=3;
               if ( strlen(sx) > 2 &&  sx[2] == '2' )
                  auswahl=4;
               break;

              case 'Y':    // Wartung Celsius Temperatur
               auswahl=5;
               if ( strlen(sx) > 2 &&  sx[2] == '2' )
                  auswahl=6;
               break;
               
              case 'U':    // Update shared memory
               auswahl=7;                               
            }
         }
      }
   }

   if ( auswahl == 1 )       // dump counter area
      dumpcounter();
   else if ( auswahl == 2)   // write counter area
      writecounter();
   else if ( auswahl == 3)   // dump regen
      dumpregen();
   else if ( auswahl == 4)   // write regen aream
      writeregen();
   else if ( auswahl == 5)   // dump Celsius Area
      dumpcelsius();
   else if ( auswahl == 6)   // clear celsius
      clearcelsius();
   else if ( auswahl == 7)   // update shared memory
      updatesharedm();   
   else if ( auswahl == 8)
      testseite();
   else
      errorseite();
   return 0;
}



/************************************************************************************
** shared memory varxx suchen
*/
int shared_varxx(void)
{
   char *s;

   shared_memory1_id = shmget((key_t)1433571165, 0, 0 );
   if (shared_memory1_id == -1)
   {
      printf("Shared memory1 shmget() failed %d<br>\n",errno);
      return 1;
   }
   s = (char*)shmat( shared_memory1_id, 0, SHM_RDONLY ); //attach
   if (s == (void *)-1)
   {
      printf("Shared memory1 shmat() failed<br>\n");
      return 1;
   }
   a_basis = (S_BASIS*)s;                  //Assign the shared_memory segment
   return 0;
}



/************************************************************************************
** shared memory temperatur suchen
*/
int shared_temperatur(void)
{
   char *s;

   shared_memory2_id = shmget((key_t)1433571166, 0, 0 );
   if (shared_memory2_id == -1)
   {
      printf("Shared memory2 shmget() failed %d<br>\n",errno);
      return 1;
   }
   s = (char*)shmat( shared_memory2_id, 0, SHM_RDONLY ); //attach
   if (s == (void *)-1)
   {
      printf("Shared memory2 shmat() failed<br>\n");
      return 1;
   }
   tdata = s;                                //Assign the shared_memory segment
   return 0;
}



/************************************************************************************
** shared memory regenwerte suchen
*/
int shared_regen(void)
{
   char *s;

   shared_memory3_id = shmget((key_t)1433571167, 0, 0 );
   if (shared_memory3_id == -1)
   {
      printf("Shared memory3 shmget() failed %d<br>\n",errno);
      return 1;
   }
   s = (char*)shmat( shared_memory3_id, 0, SHM_RDONLY ); //attach
   if (s == (void *)-1)
   {
      printf("Shared memory3 shmat() failed<br>\n");
      return 1;
   }
   regenwerte = (unsigned char*)s;           //Assign the shared_memory segment
   return 0;
}



/************************************************************************************
** shared memory counter suchen
*/
int shared_counter(void)
{
   char *s;

   shared_memory4_id = shmget((key_t)1433571168, 0, 0 );
   if (shared_memory2_id == -1)
   {
      printf("Shared memory4 shmget() failed %d<br>\n",errno);
      return 1;
   }
   s = (char*)shmat( shared_memory4_id, 0, SHM_RDONLY ); //attach
   if (s == (void *)-1)
   {
      printf("Shared memory4 shmat() failed<br>\n");
      return 1;
   }
   counter = (unsigned char*)s;                 //Assign the shared_memory segment
   return 0;
}


/************************************************************************************
** shared memory communication suchen
*/
int shared_rxdcb(void)
{
   char *s;

   shared_memory5_id = shmget((key_t)1433571169, 0, 0 );
   if (shared_memory2_id == -1)
   {
      printf("Shared memory5 shmget() failed %d<br>\n",errno);
      return 1;
   }
   s = (char*)shmat( shared_memory5_id, 0, 0 );       //attach
   if (s == (void *)-1)
   {
      printf("Shared memory5 shmat() failed<br>\n");
      return 1;
   }
   a_rdwr = (S_RDWR*)s;                 //Assign the shared_memory segment
   return 0;
}



/************************************************************************************
** Testseite
*/
void testseite( void )
{
   time_t zeit;
   char *pt;
   char *p;
   int i;

   zeit = time(NULL);
   pt = ctime(&zeit);

   print_header(0,"Testseite");                    // ohne js
   print_navi();
   printf("<br><hr>\n");

   printf("raspberrys Zeit %s<br>\n",pt);

   p = getenv("HTTP_USER_AGENT" );
   printf("HTTP_USER_AGENT %s<br>\n", p ? p : "" );
   for ( i=0; i < qanz; i++)
   {
      printf("QUERY_STRING %d: %s<br>\n", i, qx[i] ? qx[i] : "" );
   }
   print_footer();
}


/************************************************************************************
** dump counter area  Wartung Counter
*/
void dumpcounter(void)
{
   int addr,len,x,i;
   int ok=1;
   int read=0;
   char *sx;

   print_header(0,"Dump Counter");          // ohne js
   print_navi();

   if ( shared_varxx() )            // shared memory var xx
      printf("Sorry, shared varxx not found<br>\n");
   else if ( shared_counter() )     // shared memory counter
      printf("Sorry, shared counter not found<br>\n");
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           case 'R':                        // R=nnn Anzahl Bytes read
            if ( strlen(sx) > 2 )
            {
               len = a_to_i( sx+2, strlen(sx)-2);
               if ( len > 0 && len <= 256 )
                  read = 1;
               else
                  ok=0;
            }
            else
               ok=0;
            break;
           // ..........................................................
           case 'A':                        // Adresse
            if ( strlen(sx) > 2 )
            {
               addr = a_to_i( sx+2, strlen(sx)-2);
               if ( addr > 512 )
                  ok=0;
            }
            else
               ok=0;
            break;
           // ..........................................................
           default: break;
         }
      }
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Counter Read Bereich</h4>\n");
      printf("</td><tr>\n");
      printf("<form name=\"CREAD\" action=\"/cgi-bin/myinterw.cgi\">\n");
      printf("<input type=hidden name=\"C\" value=\"0\">");
      printf("<tr><td>\n");
      printf("<input type=hidden name=\"R\" value=\"256\">\n");
      printf("Adresse ");
      printf("<input type=\"radio\" name=\"A\" value=\"0\"> 0x0000\n");
      printf("<input type=\"radio\" checked name=\"A\" value=\"256\"> 0x0100\n");
      printf("<input type=\"radio\" name=\"A\" value=\"512\"> 0x0200\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"Lesen\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<textarea id=\"CA\" cols=\"50\" rows=\"18\">\n");

      for ( i=0; i < len ; i++)
      {
         if ( (i % 16) == 0 )
            printf("\n%04X ", addr);
         x = counter[ addr++ ];
         if ( (i % 4) == 0 )
            printf(" 0x%02X",x);
         else
            printf("%02X",x);
      }
      printf("\n");
      printf("</textarea>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
      printf("<p>\n");
      printf("<br><hr>\n");
   }
   print_footer();
}



/************************************************************************************
** dump counter area  Wartung Counter
*/
void dumpregen(void)
{
   int addr,len,x,i;
   int ok=1;
   int read=0;
   char *sx;
   char che[] = "checked";
   char nch[] = "";

   print_header(0,"Dump Regenmenge");          // ohne js
   print_navi();

   if ( shared_varxx() )            // shared memory var xx
      printf("Sorry, shared varxx not found<br>\n");
   else if ( shared_regen() )     // shared memory regen
      printf("Sorry, shared regenmenge not found<br>\n");
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           case 'R':                        // R=nnn Anzahl Bytes read
            if ( strlen(sx) > 2 )
            {
               len = a_to_i( sx+2, strlen(sx)-2);
               if ( len = 736 )
                  read = 1;
               else
                  ok=0;
            }
            else
               ok=0;
            break;
           // ..........................................................
           case 'A':                        // Adresse
            if ( strlen(sx) > 2 )
            {
               addr = a_to_i( sx+2, strlen(sx)-2);
               if ( addr > 3072 )
                  ok=0;
            }
            else
               ok=0;
            break;
           // ..........................................................
           default: break;
         }
      }
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Regenwerte Read Bereich</h4>\n");
      printf("0x0000 = Jahr\%4 = 0  zB: 2012, 2016, 2020 usw.<br>\n");
      printf("0x0400 = Jahr\%4 = 1  zB: 2013, 2017, 2021 usw.<br>\n");
      printf("0x0800 = Jahr\%4 = 2  zB: 2014, 2018, 2022 usw.<br>\n");
      printf("0x0C00 = Jahr\%4 = 3  zB: 2015, 2019, 2023 usw.<br>\n");
      printf("In je 2 Byte sind die Regenmesser Ticks für einen Tag abgespeichert\n");
      printf("</td><tr>\n");
      printf("<form name=\"RREAD\" action=\"/cgi-bin/myinterw.cgi\">\n");
      printf("<input type=hidden name=\"N\" value=\"0\">");
      printf("<tr><td>\n");
      printf("<input type=hidden name=\"R\" value=\"736\">\n");
      printf("Adresse ");
      if ( addr == 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"0\"> 0x0000\n",sx);
      if ( addr == 1024 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"1024\"> 0x0400\n",sx);
      if ( addr == 2048 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"2048\"> 0x0800\n",sx);
      if ( addr == 3072 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"3072\"> 0x0C00\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"Lesen\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<textarea id=\"CA\" cols=\"100\" rows=\"25\">\n");
      for ( i=0; i < len ; i++)
      {
         if ( (i % 32) == 0 )
            printf("\n%04X ", addr);
         x = regenwerte[ addr++ ];
         if ( (i % 4) == 0 )
            printf(" 0x%02X",x);
         else
            printf("%02X",x);
      }
      printf("\n");
      printf("</textarea>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
      printf("<p>\n");
      printf("<br><hr>\n");
   }
   print_footer();
}



/************************************************************************************
** password ueber MD5 setzen
** 9.1.2014 Salt request dazu
*/
int krypt_password( void )
{
   time_t current,zeit;
   unsigned int x;
   int i;
   unsigned char ok = 1;
   
   a_rdwr->command = 0x69;            // command request salt
   a_rdwr->flag = CFLAG_EXEC;         // start Kennung
   do
   {
      usleep( 66000 );                // 66msek warten
      current = time(NULL);
      if (a_rdwr->flag == CFLAG_FIN)  // rxdgmess fertig ??
         ok=0;                        // break
      else if ( current-zeit > 3 )    // maximal 3 Sekunden warten
         return 1;
   } while( ok );  
   
   x = a_rdwr->salt;
   for ( i=7; i >= 0; i-- )           // Salt in Form von 8 Zeichen
   {
      code[i] = (x & 0x0F) + 0x30;    //
      x >>= 4;
   }
   MD5Init( md5ctx );
   MD5Update( md5ctx, code, 17 );
   MD5Final ( a_rdwr->md5, md5ctx);      // MD5 cmd
   return 0;
}



/************************************************************************************
** update shared memory
*/
void updatesharedm(void)
{
   time_t zeit, current;
   char *pt;
   char *sx;
   int mem=2;
   int ok=1;
   int write=0;
   int addr=0;
   int rc=0;
   int i;
   char etext[60] = "keine Aktion";
   char che[] = "checked";
   char nch[] = "";

   zeit = time(NULL);
   pt = ctime(&zeit);
   print_header(0,"Update Shared Memory");  // ohne js
   
   printf("<script type=\"text/javascript\">\n");
   printf("function checkPIN()\n");
   printf("{\n");
   printf("var sPin = prompt(\"Bitte PIN eingeben:\");\n");   
   printf("if ( sPin.localeCompare('5432') == 0 )\n");
   printf("   return true;\n");
   printf("else \n");
   printf("   return false; \n");   
   printf("}\n");
   printf("</script>\n");
   
   print_navi();
   
   if ( shared_varxx() )                    // shared memory var xx
      printf("Sorry, shared varxx not found<br>\n");
   else if ( shared_rxdcb() )       // shared memory rxdcb
      printf("Sorry, shared rxdcb not found<br>\n");   
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           // ..........................................................
           case 'U':                        // U=0  oder U=2%M=1 bis 8
            if ( strlen(sx) == 3 )
            {
               if ( sx[2] == '2')
                  write = 1;
               else
                  ok=0;               
            }
            else
            {
               ok=0;
               sprintf(etext,"U= falsch");
            }
            break;
            
             // ..........................................................
           case 'M':                        // U=0  oder U=2%M=1 bis 8
            if ( strlen(sx) == 3 )
            {
               mem = sx[2] & 0x0F;
               if ( mem == 1 || mem == 2 || mem == 4 || mem == 8 )
                  ok=1;
               else
                   ok=0;                
            }
            break;
            
           default:
            break;
         }
      }
      if ( ok && write )
      {
         if ( krypt_password() )
             sprintf(etext,"Fertig, Salt missing");
         else    
         {
            a_rdwr->command = 60 + mem;        // command basteln
            a_rdwr->flag = CFLAG_EXEC;         // start Kennung, muss letzter Befehl sein

            do
            {
               usleep( 60000 );                // 60msek warten
               current = time(NULL);
               if (a_rdwr->flag == CFLAG_FIN)  // rxdgmess fertig ??
               {
                  if ( a_rdwr->rc == 0)
                     sprintf(etext,"Antwort ok");
                  else if ( a_rdwr->rc == 1)
                     sprintf(etext,"Fertig, rc=1");
                  else if ( a_rdwr->rc == 2)
                     sprintf(etext,"Fertig, MD5 error");
                  ok=0;                        // break
               }
               else if ( current-zeit > 3 )    // maximal 3 Sekunden warten
               {
                  ok=0;                        // break time out
                  sprintf(etext,"time out");
               }
            } while( ok );
         }         
      }
      printf("<h4>%s pid=%d</h4>\n", etext, a_rdwr->pid);
                                                           // Formular
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Update shared memory mit der entsprechenden Datei</h4>\n");
      printf("Diese Datei ist zuerst im Raspberry entsprechend bereitzustellen\n");
      printf("</td><tr>\n");
      printf("<form name=\"CR\" action=\"/cgi-bin/myinterw.cgi\" onsubmit=\"return checkPIN()\">\n");
      printf("<input type=hidden name=\"U\" value=\"2\">");
      printf("<tr><td>\n");
      printf("Shared Memory Auswahl:\n");
      if ( mem == 1 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"M\" value=\"1\"> var30\n",sx);
      if ( mem == 2 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"M\" value=\"2\"> regen\n",sx);
      if ( mem == 4 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"M\" value=\"4\"> celsius\n",sx);
      if ( mem == 8 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"M\" value=\"8\"> counter<br>\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"Write\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
      printf("<p>\n");
      printf("<br><hr>\n");
   }
   print_footer();
}



/************************************************************************************
** Seite 1 = Wartung Counter Write
*/
void writecounter( void )
{
   time_t zeit, current;
   char *pt;
   char *sx;
   int len=0,i,x;
   int ok=1;
   int aok=0;
   int write=0;
   int addr=0;
   unsigned int inf1=0,inf2=0, x1,x2;
   int rc=0;
   int t=0;
   char etext[60] = "keine Aktion";
   char che[] = "checked";
   char nch[] = "";

   zeit = time(NULL);
   pt = ctime(&zeit);

   print_header(0,"Wartung write counter");          // ohne js
   printf("<script language=\"JavaScript\" type=\"text/javascript\" src=\"/scri3.js\"></script>\n");
   print_navi();

   if ( shared_varxx() )            // shared memory var xx
      printf("Sorry, shared varxx not found<br>\n");
   else if ( shared_counter() )     // shared memory: counter
      printf("Sorry, shared counter not found<br>\n");
   else if ( shared_rxdcb() )       // shared memory rxdcb
      printf("Sorry, shared rxdcb not found<br>\n");
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           // ..........................................................
           case 'W':                        // R=n Anzahl Bytes to Write
            if ( strlen(sx) == 3 )
            {
               len = sx[2] & 0x0F;
               if ( len == 1 || len == 2 || len == 4 || len == 8 )
                  write = 1;
               else
               {
                  ok=0;
                  sprintf(etext,"length falsch");
               }
            }
            else
            {
               ok=0;
               sprintf(etext,"W= falsch");
            }
            break;
            // ..........................................................
           case 'T':                        // T Kennzeichen
            if ( strlen(sx) > 2 )
               t = a_to_i( sx+2, strlen(sx)-2);
            break;
           // ..........................................................
           case 'A':                        // Adresse
            if ( strlen(sx) > 2 )
               addr = a_to_i( sx+2, strlen(sx)-2);
            else
            {
               ok=0;
               sprintf(etext,"Adresse falsch");
            }
            break;
           // ..........................................................
           case 'I':
            if ( strlen(sx) > 2 )
               inf1 = a_to_i( sx+2, strlen(sx)-2);
            break;
           // ..........................................................
           case 'J':
            if ( strlen(sx) > 2 )
               inf2 = a_to_i( sx+2, strlen(sx)-2);
            break;
           // ..........................................................
           default: break;
         }
      }
      if ( ok && write )
      {
         x1 = inf1;
         x2 = inf2;
         if ( addr >=0 && addr <= (768 - len))
         {
            if ( len == 4 || len == 8 )
            {
               for (i=0; i < len; i++ )
               {
                  switch(i)
                  {
                    case 0: a_rdwr->reg[i] = (x1 >> 24) & 0xFF ; break;
                    case 1: a_rdwr->reg[i] = (x1 >>16)  & 0xFF ; break;
                    case 2: a_rdwr->reg[i] = (x1 >> 8)  & 0xFF ; break;
                    case 3: a_rdwr->reg[i] = x1 & 0xFF ; break;
                    case 4: a_rdwr->reg[i] = (x2 >> 24) & 0xFF ; break;
                    case 5: a_rdwr->reg[i] = (x2 >> 16) & 0xFF ; break;
                    case 6: a_rdwr->reg[i] = (x2 >> 8)  & 0xFF ; break;
                    case 7: a_rdwr->reg[i] = x2 & 0xFF ; break;
                  }
               }
            }
            else if ( len == 2 )
            {
               a_rdwr->reg[0] = (x1 >> 8)  & 0xFF ;
               a_rdwr->reg[1] = x1 & 0xFF ;
            }
            else if ( len == 1 )
            {
               a_rdwr->reg[0] = x1 & 0xFF ;
            }

            if ( krypt_password() )
                sprintf(etext,"Fertig, Salt missing");
            else    
            {    
               a_rdwr->memsw = SHR_COUNTER;       // RDWR communication area mit Leben füllen
               a_rdwr->adresse = addr;
               a_rdwr->command = 20 + len;        // command basteln
               a_rdwr->flag = CFLAG_EXEC;         // start Kennung, muss letzter Befehl sein

               do
               {
                  usleep( 60000 );                // 60msek warten
                  current = time(NULL);
                  if (a_rdwr->flag == CFLAG_FIN)  // rxdgmess fertig ??
                  {
                     if ( a_rdwr->rc == 0)
                        sprintf(etext,"Antwort ok");
                     else if ( a_rdwr->rc == 1)
                        sprintf(etext,"Fertig, rc=1");
                     else if ( a_rdwr->rc == 2)
                        sprintf(etext,"Fertig, MD5 error");
                     aok=1;
                     ok=0;                        // break
                  }
                  else if ( current-zeit > 3 )    // maximal 3 Sekunden warten
                  {
                     ok=0;                        // break time out
                     sprintf(etext,"time out");
                  }
               } while( ok );
            }
         }
      }
      printf("<h4>%s pid=%d</h4>\n", etext, a_rdwr->pid);
      printf("<h4>Adresse: 0x%04X,  L:%d, info1: 0x%08X,  info2: 0x%08X</h4>\n", addr, len, inf1, inf2);
                                                     // Formular
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Shared memory counter: Wartung (write)</h4>\n");
      printf("</td><tr>\n");
      printf("<form name=\"CR\" action=\"/cgi-bin/myinterw.cgi\" onsubmit=\"return CheckInput()\">\n");
      printf("<input type=hidden name=\"C\" value=\"2\">");
      printf("<tr><td>\n");
      printf("Write Anzahl:\n");
      if ( len == 1 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"1\"> 1\n",sx);
      if ( len == 2 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"2\"> 2\n",sx);
      if ( len == 4 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"4\"> 4\n",sx);
      if ( len == 8 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"8\"> 8 Byte<br>\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("Offset:\n");
      if ( t == 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"0\"> Gas\n",sx);
      if ( t == 4 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"4\"> Gas WW\n",sx);
      if ( t == 8 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"8\"> Strom\n",sx);
      if ( t == 12 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"12\"> Flags<br>\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("Adresse oder Monat.Jahr: <input name=\"A\" type=\"text\" size=\"10\">\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("Information 1..4: <input name=\"I\" type=\"text\" size=\"10\">\n");
      printf("Information 5..8: <input name=\"J\" type=\"text\" size=\"10\">\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"Write\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
      printf("<p>\n");
      printf("<br><hr>\n");
   }
   print_footer();
}



/************************************************************************************
** Seite 2 = Wartung Regen
*/
void writeregen(void)
{
   time_t zeit, current;
   char *pt;
   char *sx;
   int len=0,i,x;
   int ok=1;
   int aok=0;
   int write=0;
   int addr=0;
   unsigned int inf1=0,inf2=0, x1,x2;
   int rc=0;
   char etext[60] = "keine Aktion";
   char che[] = "checked";
   char nch[] = "";

   zeit = time(NULL);
   pt = ctime(&zeit);

   print_header(0,"Wartung Regen");
   printf("<script language=\"JavaScript\" type=\"text/javascript\" src=\"/scri3.js\"></script>\n");
   print_navi();

   if ( shared_varxx() )            // shared memory var xx
      printf("Sorry, shared Memory1 not found<br>\n");
   else if ( shared_regen() )     // shared memory counter
      printf("Sorry, shared regenmenge not found<br>\n");
   else if ( shared_rxdcb() )       // shared memory rxdcb
      printf("Sorry, shared rxdcb not found<br>\n");
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           // ..........................................................
           case 'W':                        // R=n Anzahl Bytes to Write
            if ( strlen(sx) == 3 )
            {
               len = sx[2] & 0x0F;
               if ( len == 1 || len == 2 || len == 4 || len == 8 )
                  write = 1;
               else
               {
                  ok=0;
                  sprintf(etext,"length falsch");
               }
            }
            else
            {
               ok=0;
               sprintf(etext,"W= falsch");
            }
            break;
           // ..........................................................
           case 'A':                        // Adresse
            if ( strlen(sx) > 2 )
               addr = a_to_i( sx+2, strlen(sx)-2);
            else
            {
               ok=0;
               sprintf(etext,"Adresse falsch");
            }
            break;
           // ..........................................................
           case 'I':
            if ( strlen(sx) > 2 )
               inf1 = a_to_i( sx+2, strlen(sx)-2);
            break;
           // ..........................................................
           case 'J':
            if ( strlen(sx) > 2 )
               inf2 = a_to_i( sx+2, strlen(sx)-2);
            break;
           // ..........................................................
           default: break;
         }
      }
      if ( ok && write )
      {
         x1 = inf1;
         x2 = inf2;
         if ( addr >=0 && addr <= (4096 - len))
         {
            if ( len == 4 || len == 8 )
            {
               for (i=0; i < len; i++ )
               {
                  switch(i)
                  {
                    case 0: a_rdwr->reg[i] = (x1 >> 24) & 0xFF ; break;
                    case 1: a_rdwr->reg[i] = (x1 >>16)  & 0xFF ; break;
                    case 2: a_rdwr->reg[i] = (x1 >> 8)  & 0xFF ; break;
                    case 3: a_rdwr->reg[i] = x1 & 0xFF ; break;
                    case 4: a_rdwr->reg[i] = (x2 >> 24) & 0xFF ; break;
                    case 5: a_rdwr->reg[i] = (x2 >> 16) & 0xFF ; break;
                    case 6: a_rdwr->reg[i] = (x2 >> 8)  & 0xFF ; break;
                    case 7: a_rdwr->reg[i] = x2 & 0xFF ; break;
                  }
               }
            }
            else if ( len == 2 )
            {
               a_rdwr->reg[0] = (x1 >> 8)  & 0xFF ;
               a_rdwr->reg[1] = x1 & 0xFF ;
            }
            else if ( len == 1 )
            {
               a_rdwr->reg[0] = x1 & 0xFF ;
            }

            if ( krypt_password() )
                sprintf(etext,"Fertig, Salt missing");
            else    
            { 
               a_rdwr->memsw   = SHR_REGEN;       // RDWR communication area mit Leben füllen
               a_rdwr->adresse = addr;
               a_rdwr->command = 20 + len;        // command basteln
               a_rdwr->flag    = CFLAG_EXEC;      // start Kennung, muss letzter Befehl sein

               do
               {
                  usleep( 60000 );                // 60msek warten
                  current = time(NULL);
                  if (a_rdwr->flag == CFLAG_FIN)  // rxdgmess fertig ??
                  {
                     if ( a_rdwr->rc == 0)
                        sprintf(etext,"Antwort ok");
                     else if ( a_rdwr->rc == 1)
                        sprintf(etext,"Fertig, rc=1");
                     else if ( a_rdwr->rc == 2)
                        sprintf(etext,"Fertig, MD5 error");
                     aok=1;
                     ok=0;                        // break
                  }
                  else if ( current-zeit > 3 )    // maximal 3 Sekunden warten
                  {
                     ok=0;                        // break time out
                     sprintf(etext,"time out");
                  }
               } while( ok );
            }
         }
      }
      printf("<h4>%s pid=%d</h4>\n", etext, a_rdwr->pid);
      printf("<h4>Adresse: 0x%04X,  L:%d, info1: 0x%08X,  info2: 0x%08X</h4>\n", addr, len, inf1, inf2);
                                                     // Formular
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Regen Update</h4>\n");
      printf("</td><tr>\n");
      printf("<form name=\"NR\" action=\"/cgi-bin/myinterw.cgi\" onsubmit=\"return CheckInput_N()\">\n");
      printf("<input type=hidden name=\"N\" value=\"2\">\n");
      printf("<tr><td>\n");
      printf("Write Anzahl:\n");
      if ( len == 1 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"1\"> 1\n",sx);
      if ( len == 2 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"2\"> 2\n",sx);
      if ( len == 4 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"4\"> 4\n",sx);
      if ( len == 8 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"W\" value=\"8\"> 8 Byte<br>\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("Adresse oder Tag.Monat.Jahr: <input name=\"A\" type=\"text\" size=\"10\">\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("Info 1 .. 4: <input name=\"I\" type=\"text\" size=\"10\">\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("nfo 5 .. 8: <input name=\"J\" type=\"text\" size=\"10\">\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"senden\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
   }
   print_footer();
}



/************************************************************************************
** Wartung Temperatur Werte, Dump Speicher Bereiche je 6 Stunden
*/
void dumpcelsius(void)
{
   int i,addr,ax,len,t=0,day,ta=0,ts=0;
   int ok=1;
   int read=0;
   char *sx;
   char c;
   char che[] = "checked";
   char nch[] = "";

   print_header(0,"Dump Celsiuswerte");          // ohne js
   print_navi();

   if ( shared_varxx() )            // shared memory var xx
      printf("Sorry, shared varxx not found<br>\n");
   else if ( shared_temperatur() )     // shared memory regen
      printf("Sorry, shared Temperatur not found<br>\n");
   else
   {
      for ( i=0; i < qanz; i++)             // loop durch die query
      {
         sx = qx[i];
         switch ( sx[0] )
         {
           // ..........................................................
           case 'R':                        // R=nnn Anzahl Bytes read
            if ( strlen(sx) > 2 )
            {
               len = a_to_i( sx+2, strlen(sx)-2);
               if ( len = 1440 )
                  read = 1;
               else
                  ok=0;
            }
            else
               ok=0;
            break;
           // ..........................................................
           case 'A':                        // Adress offset 12 Uhr
            if ( strlen(sx) > 2 )
            {
               addr = a_to_i( sx+2, strlen(sx)-2);
               ax = addr;                   // merke auswahl 
               if ( addr > 1440 )
                  ok=0;
            }
            else
               ok=0;
            break;
            // ..........................................................
           case 'D':                        // Day Gestern oder heute
            if ( strlen(sx) == 3 )
            {
               day = sx[2] & 0x0f;             // 0 bis 4
               if ( day == 0  || day == 1 )
               {
                  ts = a_basis->temperatur_switch;
                  ts ^= day;                   // gestern = 1
                  ts *= 16384;                  
               }
               else
                  ok=0;               
            }
            else
               ok=0;
            break;
            // ..........................................................
            case 'T':                        // Temperatur Auswahl
            if ( strlen(sx) == 3 )
            {
               t = sx[2] & 0x0f;             // 0 bis 4
               switch( t )
               {
                 case 0: case 1: case 2: case 3: case 4: ta = 3072*t; break;                  
                 default:  ok=0; break;
               }  
            }
            else
               ok=0;
            break;
           // ..........................................................
           default: break;
         }
      }      
      addr += ta + ts ;
      
      printf("<table align=\"center\">\n");
      printf("<tr><td>\n");
      printf("<h4>Temperaturwerte Read Bereich</h4>\n");
      
      printf("In je 2 Byte sind die Temperaturwerte für eine Minute abgespeichert\n");
      printf("</td><tr>\n");
      printf("<form name=\"TREAD\" action=\"/cgi-bin/myinterw.cgi\">\n");
      printf("<input type=hidden name=\"Y\" value=\"0\">");
      printf("<input type=hidden name=\"R\" value=\"1440\">\n");
      
      printf("<tr><td>\n");      
      printf("Tag Auswahl ");
      if ( day == 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"D\" value=\"0\"> Heute\n",sx);
      if ( day == 1 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"D\" value=\"1\"> Gestern\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      
      printf("<tr><td>\n");      
      printf("Zeitbereich");
      if ( ax == 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"0\"> 0:00-11:59\n",sx);
      if ( ax > 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"A\" value=\"1440\"> 12:00-23:59\n",sx);
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      
      printf("Temperatur ");
      if ( t == 0 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"0\"> Vorlauf\n",sx);
      if ( t == 1 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"1\"> Rücklauf\n",sx);
      if ( t == 3 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"3\"> Warmwasser\n",sx);
      if ( t == 2 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"2\"> Aussen\n",sx);
      if ( t == 4 ) sx = che; else sx = nch;
      printf("<input type=\"radio\" %s name=\"T\" value=\"4\"> Gewächshaus\n",sx);
      printf("</td></tr>\n");
      
      printf("<tr><td>\n");
      printf("<input type=\"submit\" value=\"Lesen\">\n");
      printf("</form>\n");
      printf("</td></tr>\n");
      printf("<tr><td>\n");
      printf("<textarea id=\"CA\" cols=\"80\" rows=\"36\">\n");
      printf("addr  0.:.:.:.:.5.:.:.:.:. 10:.:.:.:.15:.:.:.:. 20:.:.:.:.25:.:.:.:. Minuten");
      for ( i=0; i < len ; i++)
      {
         if ( (i % 60) == 0 )
         {
            if ( (i % 120) == 0  && i > 0)
               printf("\n\n%04X ", addr);
            else
               printf("\n%04X ", addr);            
         }
         c = tdata[ addr++ ];
         if ( (i % 20) == 0 )        // jeweils 10 Minuten 
            printf(" %c", c);
         else
            printf("%c", c);
      }
      printf("\n");
      printf("</textarea>\n");
      printf("</td></tr>\n");
      printf("</table>\n");
      printf("<p>\n");
      printf("<br><hr>\n");
   }
   print_footer();
}



/************************************************************************************
** Wartung Temperatur Werte, Dump Speicher Bereiche je 6 Stunden
*/
void clearcelsius(void)
{
}



/************************************************************************************
** Status Seite
*/
void errorseite(void)
{
   print_header(0,"Error");
   print_navi();
   printf("<h3>Seite nicht definiert, query falsch</h3><br>");
   print_footer();
}



/*****************************************************************************
**  ascii to int
*/
unsigned int a_to_i( char *quelle, int anzahl)
{
   unsigned int x=0;
   unsigned char n;

   for ( n=0; n < anzahl; n++)
   {
      x *= 10;
      x += *quelle & 0x0F;
      quelle++;
   }
   return x;
}



/************************************************************************************
** header und navigation senden
*/
void print_header( int jssw, char *title )
{
   printf("Content-Type: text/html\n\n");
   printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
   printf("<html>\n");
   printf("<head>\n");
   printf("<meta http-equiv=\"content-type\" content=\"text/html; charset=ISO-8859-1\">\n");
   //printf("<meta http-equiv=\"refresh\" content=\"60; URL=index.htm\">\n");
   printf("<meta http-equiv=\"expires\" content=\"0\">\n");
   printf("<title>Raspberry %s</title>\n",title);
   printf("<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\">\n");
   if ( jssw == 1 )
      printf("<script language=\"JavaScript\" type=\"text/javascript\" src=\"/scri1.js\"></script>\n");
   else if ( jssw == 2 )
      printf("<script language=\"JavaScript\" type=\"text/javascript\" src=\"/scri2.js\"></script>\n");
}



/************************************************************************************
** header und navigation senden
** 7.1.2014 Erweiterung mit nav2
*/
void print_navi( void )
{
   printf("</head>\n");
   printf("<body>\n");
   printf("<h1>Raspberry PI web server</h1>\n");
   printf("<div id=\"nav\">\n");
   printf("<a href=\"/cgi-bin/myinter.cgi\">PI Home</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?S\">Status</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?H\">Heizung</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?H=18\">18 Monate</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?R\">Regen 33 Tage</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?N\">Regen 18 Monate</a>\n");
   printf("<a href=\"/cgi-bin/myinter.cgi?T=1&Z=0\">°C Grafik</a>\n");
   printf("<a href=\"/cgi-bin/myinterw.cgi?C=0&A=0&R=256\">Wartung</a>\n");
   printf("</div>\n");
   printf("<br>");
   printf("<h2>Shared Memory Wartung</h2>");
   printf("<div id=\"nav2\">\n");
   printf("<a href=\"/cgi-bin/myinterw.cgi?C=0&A=0&R=256\">Rd. shm. counter</a>");
   printf("<a href=\"/cgi-bin/myinterw.cgi?C=2\">Wr. shm. counter</a>");
   printf("<a href=\"/cgi-bin/myinterw.cgi?N=0&A=0&R=736\">Rd. shm. Regenmenge</a>");
   printf("<a href=\"/cgi-bin/myinterw.cgi?N=2\">Wr. shm. Regenmenge</a>");
   printf("<a href=\"/cgi-bin/myinterw.cgi?Y=0\">Rd. shm. Celsius</a>");
   printf("<a href=\"/cgi-bin/myinterw.cgi?U=0\">Update shared Memory</a>");
   printf("</div>");
   printf("<div id=\"main\">\n");
}



/************************************************************************************
** ende der html Seite
*/
void print_footer(void)
{
   printf("<br>\n");
   printf("</div>\n");
   printf("<div id=\"tail\"><a href=\"http://www.ralidzba.de\">www.ralidzba.de</a>Reinhard Alfons Lidzba 2014</div>\n");
   printf("</body>\n");
   printf("</html>\n");
}



/*
**  Anpassung an Intel Byte Notation
*/
static void byteReverse ( unsigned char *buf, unsigned long longs)
{
   unsigned long t;
   unsigned char test[5] = { 0x01,0x02,0x03,0x04 };
   unsigned long *p ;

   p = (unsigned long*)test;
   t = *p & 0xff ;

   switch ( t )
   {
     case 1:         /* intel normal */
       break;

     case 4:
       p = (unsigned long*)buf;
       do
       {
          t = *p++ ;
          *buf++ = (unsigned char )((t ) & 0xFF);
          *buf++ = (unsigned char )((t >> 8 )  & 0xFF) ;
          *buf++ = (unsigned char )((t >> 16 ) & 0xFF) ;
          *buf++ = (unsigned char )((t >> 24 ) & 0xFF) ;
       } while( longs--);
       break;
   }
}



/*
** Start der MD5-Akkumulation. Setzt den Bit-Zaehler auf 0
** und initialisiert die MD5-Konstanten
*/
void MD5Init( MD5Context *ctx )
{
   ctx->buf[0] = 0x67452301;
   ctx->buf[1] = 0xefcdab89;
   ctx->buf[2] = 0x98badcfe;
   ctx->buf[3] = 0x10325476;

   ctx->bits[0] = 0;
   ctx->bits[1] = 0;
}


/*
**  Update des Kontexts
*/
void MD5Update( MD5Context *ctx, unsigned char *buf, unsigned long len)
{
   unsigned long t;

                         // Update bitcount
   t = ctx->bits[0];

   if ( (ctx->bits[0] = t + ((unsigned long) len << 3)) < t )
      ctx->bits[1]++;

   ctx->bits[1] += len >> 29;
   t = (t >> 3) & 0x3f;
   if (t)
   {
      unsigned char *p = (unsigned char *) ctx->in + t;
      t = 64 - t;
      if (len < t)
      {
         memcpy(p, buf, len);
         return;
      }
      memcpy(p, buf, t);

      byteReverse(ctx->in, 16);

      MD5Transform( ctx->buf, (unsigned long *) ctx->in);

      buf += t;
      len -= t;
   }

                         // Erstellen der 64-byte Bloecke
   while (len >= 64)
   {
      memcpy(ctx->in, buf, 64);
      byteReverse(ctx->in, 16);
      MD5Transform(ctx->buf, (unsigned long *) ctx->in);
      buf += 64;
      len -= 64;
   }

   /* Handle any remaining bytes of data. */
   memcpy(ctx->in, buf, len);
}



/* ausgabe des ergebnisses
 */
void MD5Final( unsigned char digest[16], MD5Context *ctx )
{
   unsigned count;
   unsigned char *p;

                           // Berechne die Anzahl der Bytes mod 64
   count = (ctx->bits[0] >> 3) & 0x3F;

   /*
    * Zuerst wird mit 0x80 aufgefuellt, weil immer mindestens ein Byte
    * frei ist
    */

   p = ctx->in + count;

   *p++ = 0x80;
   /* Bytes um auf 64 bytes aufzufuellen */
   count = 64 - 1 - count;
   /* Auffuellen bis auf 56 mod 64 */

   if (count < 8)
   {
       /* Fuelle den ersten Block auf 64 bytes */

       memset(p, 0, count);
       byteReverse(ctx->in, 16);
       MD5Transform(ctx->buf, (unsigned long *) ctx->in);

       /* Fuelle den naechsten Block mit 56 bytes */
       memset(ctx->in, 0, 56);
   }
   else
   {
      memset(p, 0, count - 8);
   }
   byteReverse(ctx->in, 14);

   /* haengt die Laenge Nachricht an */

   ((unsigned long *) ctx->in)[14] = ctx->bits[0];
   ((unsigned long *) ctx->in)[15] = ctx->bits[1];

   MD5Transform(ctx->buf, (unsigned long *) ctx->in);

   byteReverse((unsigned char *) ctx->buf, 4); /* ergebnis verdrehen */
   memcpy(digest, ctx->buf, 16);               /* MD5 Ergebnis       */
   memset(ctx, 0, sizeof(ctx));
}


/*
** Die vier Kernfunktionen des Algorithmus
*/
#define F1(x, y, z) ((x & y) | (~x & z))
#define F2(x, y, z) ((x & z) | (y & ~z))
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))


/*
** Der zentrale Schritt des Algorithmus
*/
#define MD5STEP(f, w, x, y, z, data, s)  ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )


/*
** Dies ist der Kern des MD5-Algorithmus je 64 Byte input data
*/
static void MD5Transform( unsigned long buf[4], unsigned long const in[16] )
{
   unsigned long a, b, c, d;

   a = buf[0];
   b = buf[1];
   c = buf[2];
   d = buf[3];

   MD5STEP(F1, a, b, c, d, in[0]  + 0xd76aa478, 7);
   MD5STEP(F1, d, a, b, c, in[1]  + 0xe8c7b756, 12);
   MD5STEP(F1, c, d, a, b, in[2]  + 0x242070db, 17);
   MD5STEP(F1, b, c, d, a, in[3]  + 0xc1bdceee, 22);
   MD5STEP(F1, a, b, c, d, in[4]  + 0xf57c0faf, 7);
   MD5STEP(F1, d, a, b, c, in[5]  + 0x4787c62a, 12);
   MD5STEP(F1, c, d, a, b, in[6]  + 0xa8304613, 17);
   MD5STEP(F1, b, c, d, a, in[7]  + 0xfd469501, 22);
   MD5STEP(F1, a, b, c, d, in[8]  + 0x698098d8, 7);
   MD5STEP(F1, d, a, b, c, in[9]  + 0x8b44f7af, 12);
   MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
   MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
   MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
   MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
   MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
   MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

   MD5STEP(F2, a, b, c, d, in[1]  + 0xf61e2562, 5);
   MD5STEP(F2, d, a, b, c, in[6]  + 0xc040b340, 9);
   MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
   MD5STEP(F2, b, c, d, a, in[0]  + 0xe9b6c7aa, 20);
   MD5STEP(F2, a, b, c, d, in[5]  + 0xd62f105d, 5);
   MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
   MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
   MD5STEP(F2, b, c, d, a, in[4]  + 0xe7d3fbc8, 20);
   MD5STEP(F2, a, b, c, d, in[9]  + 0x21e1cde6, 5);
   MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
   MD5STEP(F2, c, d, a, b, in[3]  + 0xf4d50d87, 14);
   MD5STEP(F2, b, c, d, a, in[8]  + 0x455a14ed, 20);
   MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
   MD5STEP(F2, d, a, b, c, in[2]  + 0xfcefa3f8, 9);
   MD5STEP(F2, c, d, a, b, in[7]  + 0x676f02d9, 14);
   MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

   MD5STEP(F3, a, b, c, d, in[5]  + 0xfffa3942, 4);
   MD5STEP(F3, d, a, b, c, in[8]  + 0x8771f681, 11);
   MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
   MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
   MD5STEP(F3, a, b, c, d, in[1]  + 0xa4beea44, 4);
   MD5STEP(F3, d, a, b, c, in[4]  + 0x4bdecfa9, 11);
   MD5STEP(F3, c, d, a, b, in[7]  + 0xf6bb4b60, 16);
   MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
   MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
   MD5STEP(F3, d, a, b, c, in[0]  + 0xeaa127fa, 11);
   MD5STEP(F3, c, d, a, b, in[3]  + 0xd4ef3085, 16);
   MD5STEP(F3, b, c, d, a, in[6]  + 0x04881d05, 23);
   MD5STEP(F3, a, b, c, d, in[9]  + 0xd9d4d039, 4);
   MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
   MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
   MD5STEP(F3, b, c, d, a, in[2]  + 0xc4ac5665, 23);

   MD5STEP(F4, a, b, c, d, in[0]  + 0xf4292244, 6);
   MD5STEP(F4, d, a, b, c, in[7]  + 0x432aff97, 10);
   MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
   MD5STEP(F4, b, c, d, a, in[5]  + 0xfc93a039, 21);
   MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
   MD5STEP(F4, d, a, b, c, in[3]  + 0x8f0ccc92, 10);
   MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
   MD5STEP(F4, b, c, d, a, in[1]  + 0x85845dd1, 21);
   MD5STEP(F4, a, b, c, d, in[8]  + 0x6fa87e4f, 6);
   MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
   MD5STEP(F4, c, d, a, b, in[6]  + 0xa3014314, 15);
   MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
   MD5STEP(F4, a, b, c, d, in[4]  + 0xf7537e82, 6);
   MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
   MD5STEP(F4, c, d, a, b, in[2]  + 0x2ad7d2bb, 15);
   MD5STEP(F4, b, c, d, a, in[9]  + 0xeb86d391, 21);

   buf[0] += a;
   buf[1] += b;
   buf[2] += c;
   buf[3] += d;
}
