home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 December / PCWorld_2000-12_cd.bin / Komunikace / Comanche / plugins / apache / apacheDumper.tcl < prev    next >
Text File  |  2000-11-02  |  16KB  |  589 lines

  1.  
  2.  
  3. # Things we want to take into account
  4. # - How to deal with ncluded files
  5. # - How to deal with name based virtual hosts
  6. # - How to deal with some directives being disabled
  7.  
  8. # Included file
  9. #   - Up to a certain point included files are as if they were
  10. #   in the same file, so we could in theory just process it as usual
  11. #   - Name based virtual hosts.
  12. #     If we could deliver the parsing in chunks (list of lines) and return 
  13. # in chunks, we could:  solve the virtual host stuff and also more easy for 
  14. # saving Include files
  15. #
  16. #  Available vars:
  17. #  currentXmlDirectives: List with all xmlDirectives 
  18. #                 not yet dumped for the currentScope 
  19. #  currentContainer: currentContainer
  20. #  
  21. #  currentContainerStack
  22. #  xmlDirectivesStack     (to move up and down between sections)
  23. #  
  24. #  parseFile [split [read httpd.conf] \n]
  25. #  parseFile $httpd.conf
  26. #  
  27. #  
  28. # parseText --
  29. #  
  30. #  while [llength $lines] {
  31. #      set data [getLine $lines]
  32. #      set info [type $data]
  33. #      
  34. #      # info contains: {type extra_data}
  35. #      
  36. #      switch [lindex $info 1] {
  37. #     comment {
  38. #        append buffer
  39. #     } include {
  40. #        # needs to be defined
  41. #     } beginningOfSection {
  42. #        get all lines until end of section
  43. #        processSection
  44. #     } directive {
  45. #        processDirective
  46. #     }
  47. #      }     
  48. #  }
  49. #  
  50. #
  51. #  processDirective {} {
  52. #      # Belongs to a disabled module?
  53. #      #     yes -> return
  54. #      #     no -> continue
  55. #      # Exists specialCase?
  56. #      # Exists entry in xmlDirectives?
  57. #      # Exists unknown?
  58. #      # If not, ignore it
  59. #   
  60. #  }  
  61.       
  62.   
  63.  
  64. class apachePrettyDumper {
  65.     variable specialCaseMapping
  66.     variable specialCaseDirectiveMapping
  67.     variable currentXmlDirectives
  68.     variable currentContainer
  69.     variable currentContainerStack
  70.     variable containerList
  71.     variable containerListStack
  72.     variable xmlDirectivesStack
  73.     variable xmlConfDoc
  74.  
  75.     # need to know if directives enabled or not
  76.  
  77.     public variable moduleManager
  78.  
  79.     # For include files
  80.     variable currentFile
  81.     
  82.     # In Apache all the includes are relative to serveroot, so we do not really need
  83.     # currentFile
  84.  
  85.     public variable includeroot
  86.  
  87.     constructor {} {
  88.     
  89.     # Have to made them global, if inherited 
  90.     # by sambaParser does not work
  91.  
  92.     set currentContainerStack [stack ::#auto]
  93.     set xmlDirectivesStack [stack ::#auto]
  94.     set containerListStack [stack ::#auto]
  95.     }
  96.     method parseText {lines} 
  97.     method isSpecialCase 
  98.     method setSpecialCase
  99.     method setSpecialCaseDirectiveMapping
  100.     method getLine
  101.     method getTypeOfLine
  102.     method processDirective
  103.     method processSection
  104.     method getXmlDirectivesWithThatName
  105.     method dumpDirective
  106.     method dumpSpecialCase
  107.     method dumpContainer
  108.     method dump
  109.     method dumpFile
  110.     method dumpRest
  111.     method getSection
  112. }
  113.  
  114. body apachePrettyDumper::dump { xmlConfDocument fileName } {
  115.     # XXX Catch here when file not writable
  116.     if ![file writable $fileName] {
  117.        MessageDlg .d -message "File $fileName not writable by current user!" -title Error -type ok -icon error
  118.        return
  119.     }
  120.     # Set up currentXmlDirectives
  121.     set xmlConfDoc $xmlConfDocument
  122.     set currentContainer [$xmlConfDoc getRootContainer]
  123.     set containerList [$xmlConfDoc getContainers $currentContainer]
  124.     set currentXmlDirectives [$xmlConfDoc getDirectives $currentContainer] 
  125.     set currentFile $fileName
  126.     if [catch {dumpFile $fileName 1} kk] {
  127.        MessageDlg .d -message "Error ocurred when writing configuration file\n$kk" -title Error -type ok -icon error
  128.        return       
  129.     }
  130. }
  131.  
  132. # Main tells if it is the main httpd.conf so anything that remains, we dump there
  133.  
  134. body apachePrettyDumper::dumpFile { fileName {main 0}} {
  135.     set currentFile $fileName
  136.     set f [open $fileName r]
  137.     set text [read $f]
  138.     close $f
  139.     set result [parseText [split $text \n ]]
  140.     if $main {
  141.     append result [dumpRest]
  142.     }
  143.     set f [open $fileName w]
  144.     puts $f $result
  145.     close $f
  146. }
  147.  
  148. body apachePrettyDumper::parseText { lineList } {
  149.     set result {}   
  150.     while {[llength $lineList]} {
  151.     set data [getLine lineList]
  152.     set info [getTypeOfLine $data]
  153.     # info contains: {type ?extra_data?}
  154.  
  155.     switch [lindex $info 0] {
  156.         comment {
  157.         append result $data\n
  158.         } include {
  159.         set includeFile [lindex $info 1]
  160.         set tmpFile $currentFile
  161.         switch [file pathtype $includeFile] {
  162.             absolute {
  163.             
  164.             # Do nothing, we can open it
  165.             
  166.             $this dumpFile $includeFile
  167.             } relative {
  168.             
  169.             # Ok, we need to prepend the conf directives dir
  170.             
  171.             set includeFile [file join $includeroot $includeFile]
  172.             if ![file exists $includeFile] {
  173.                 puts "Include file $includeFile could not be processed. \
  174.                     Check that it exists and has the right permissions"
  175.             } else {
  176.                   $this dumpFile $includeFile
  177.                   }
  178.             } volumerelative {
  179.             
  180.             # Um, unsure about what volume relative is.
  181.             
  182.             puts "Include path was volume relative"
  183.             } default {
  184.             error "Unknown path type"
  185.             }        
  186.         }
  187.         set currentFile $tmpFile
  188.         } beginSection {
  189.  
  190.         # sectionInfo = {value class}
  191.  
  192.         set sectionInfo [lrange $info 1 2]
  193.  
  194.         # Returns the lines belonging to the section and 
  195.         # the rest
  196.  
  197.         set sectionResult [getSection $sectionInfo $lineList]
  198.         foreach {section lineList} $sectionResult break;
  199.         append result [processSection $sectionInfo $section]
  200.         } directive {
  201.         append result [processDirective $data]
  202.         } default {
  203.         error "Encountered unexpected [lindex $info 0]"
  204.         }
  205.         
  206.     }
  207.      }
  208.  
  209.     return $result
  210. }
  211.  
  212.  
  213. # getSection --
  214. #    We have detected the beginning of a section. Now we want to return the lines
  215. # inside the section. It is necessary to abstract this interface, as Samba does not
  216. # mark the end of a section.
  217. #  
  218. # sectionInfo 
  219. # lineList 
  220. #
  221. # returns 
  222. #    sectionLines : lines belonging to the section
  223. #    rest:  rest of lines
  224.  
  225. body apachePrettyDumper::getSection {sectionInfo lineList} {
  226.     set data [getLine lineList]
  227.     set info [getTypeOfLine $data]
  228.     set section {}
  229.     set sectionLines {}
  230.     # Only interested in endOfSection of same type, so we can anidate requests
  231.     
  232.     # We need to take into account recursive sections, like ifModule sections
  233.     # Any time we encounter a section of the same type we are configuring, count increments
  234.     # Any time we encounter a endSection we decrement
  235.     # To get out of the loop count must be 0
  236.     
  237.     set sectionTypeMain [string tolower [lindex $sectionInfo 1]]
  238.     set typeOfLine [lindex $info 0]
  239.     set sectionType [string tolower [lindex $info 1]]
  240.     set count 0
  241.     while {![expr [string match $typeOfLine endSection] \
  242.         && [ string match $sectionTypeMain $sectionType ] && !$count]} {
  243.     lappend sectionLines $data
  244.     set data [getLine lineList]
  245.     set info [getTypeOfLine $data]
  246.     set typeOfLine [lindex $info 0]
  247.     set sectionType [string tolower [lindex $info 1]]
  248.     if {[expr [string match $typeOfLine beginSection] && [string match $sectionTypeMain $sectionType]]} {
  249.         incr count
  250.     }
  251.     if {[expr [string match $typeOfLine endSection] && [string match $sectionTypeMain $sectionType ]]} {
  252.        if {$count > 1} {
  253.           incr count -1
  254.        }
  255.     }
  256.     }
  257.     return [list $sectionLines $lineList ]
  258. }
  259.  
  260. body apachePrettyDumper::dumpRest {} {
  261.     set result \n
  262.     
  263.     # Now we have left the ones that were not found in the template
  264.  
  265.     foreach directive $currentXmlDirectives {
  266.     set dirName [string tolower [$directive getName]]
  267.  
  268.     # Skip disabled directives
  269.  
  270.      if ![$moduleManager isDirectiveEnabled $dirName] {
  271.         continue
  272.      }  
  273.     if [$directive doYouBelongTo unknownDirective] {
  274.         debug "dumping unknown in dumpRest $directive - [$directive getValue]"
  275.         append result [$directive getValue]\n
  276.     } elseif [info exists specialCaseMapping($dirName)] {
  277.         append result [dumpSpecialCase $dirName $directive]
  278.     } else {
  279.         append result [dumpDirective $directive]
  280.     }
  281.     }
  282.  
  283.     # Same goes with containers
  284.  
  285.     foreach one $containerList {
  286.     append result [dumpContainer $one]
  287.     set idx [lsearch -exact $containerList $one]
  288.     set containerList [lreplace $containerList $idx $idx]                 
  289.     }
  290.     return $result
  291. }
  292.  
  293. body apachePrettyDumper::dumpContainer {container} {
  294.     set result {}
  295.     append result "<[$container getClasses] [$container getName]>\n"
  296.     foreach directive [$xmlConfDoc getDirectives $container] {
  297.     set dirName [string tolower [$directive getName]]
  298.      if [$directive doYouBelongTo unknownDirective] {
  299.          append result [$directive getValue]\n
  300.      } elseif [info exists specialCaseMapping($dirName)] {
  301.         append result [dumpSpecialCase $dirName $directive]  
  302.     } else {
  303.         append result [dumpDirective $directive]
  304.     }     
  305.     }
  306.     foreach childContainer [$xmlConfDoc getContainers $container] {
  307.     append result [ dumpContainer $childContainer]
  308.     }
  309.     append result "</[$container getClasses]>\n"  
  310.     return $result
  311. }
  312.  
  313. # sectionInfo is a list containing {value class}
  314.  
  315. body apachePrettyDumper::processSection {sectionInfo data} {
  316.     
  317.     set result {}
  318.     set value [lindex $sectionInfo 0]
  319.     set class [lindex $sectionInfo 1]
  320.  
  321.     # Save previous state
  322.  
  323.     $xmlDirectivesStack push $currentXmlDirectives
  324.     $currentContainerStack push $currentContainer
  325.  
  326.     # Search for containers with same class and value
  327.     #      How many?
  328.     #          0 -> Do nothing
  329.     #          1 -> Just use that one
  330.     #          >1 -> Is a virtual host?
  331.     #                check serverName
  332.     #                    matches?
  333.     #                    Yes: use that
  334.     #                    none matches: forget
  335.  
  336.     set matchingContainers {}
  337.     foreach one $containerList {
  338.     set containerClass [$one getClasses] 
  339.     if [string match $containerClass $class] {
  340.         if  [string match [$one getName] $value] {
  341.         lappend matchingContainers $one
  342.         }
  343.     }
  344.     }
  345.  
  346.     switch [llength $matchingContainers] {
  347.     0 {
  348.  
  349.         # Do nothing
  350.  
  351.     } 1 {
  352.  
  353.         set matchingContainer $matchingContainers
  354.  
  355.         # Remove container from list
  356.  
  357.         set idx [lsearch -exact $containerList $matchingContainer]
  358.         set containerList [lreplace $containerList $idx $idx]                 
  359.         $containerListStack push $containerList
  360.  
  361.         set currentXmlDirectives [$xmlConfDoc getDirectives $matchingContainer]
  362.         set currentContainer $matchingContainer
  363.         set containerList [$xmlConfDoc getContainers $matchingContainer]
  364.  
  365.         append result "<[$matchingContainer getClasses] [$matchingContainer getName]>\n"
  366.  
  367.         append result [parseText $data]
  368.         append result [dumpRest]
  369.         append result "</[$matchingContainer getClasses]>\n" 
  370.  
  371.         set containerList [$containerListStack pop]        
  372.     } default {
  373.         
  374.         set commented {
  375.  
  376.         # By now, just ignore 
  377.         # To-do finish this
  378.         # Address name based virtualhost
  379.         
  380.         if [string match $class virtualhost] {
  381.             # Check for servernames
  382.         }
  383.         }
  384.     }
  385.     }
  386.  
  387.     set currentXmlDirectives [$xmlDirectivesStack pop]
  388.     set currentContainer [$currentContainerStack pop]
  389.  
  390.  
  391.     return $result
  392. }
  393.  
  394. body apachePrettyDumper::getLine { lineList } {
  395.     upvar $lineList list
  396.     set result [lindex $list 0]
  397.     set list [lrange $list 1 end]
  398.     return $result
  399. }
  400.  
  401. body apachePrettyDumper::getTypeOfLine { line } {
  402.     set data [string trim $line]
  403.     if {[regexp "^#+" $data] || ![string length $data]} {
  404.     return comment
  405.     } elseif [regexp -nocase "^include (.*)" $data dummy fileName] {
  406.     return [list include $fileName]
  407.     } elseif [regexp "^ *</(.*)>+$" $data dummy class ] {
  408.     return [list endSection $class]
  409.     } elseif [regexp "^ *<+.*>+$" $data] {
  410.     regexp {<([^ ]*) (.*)>}  $data dummy class value
  411.     return [list beginSection  $value [string tolower $class]]
  412.     } else {
  413.     return directive
  414.     }       
  415.  
  416. }
  417.  
  418. body apachePrettyDumper::processDirective {data} {
  419.  
  420.     set result {}
  421.  
  422.     # TODO: check if belongs to disabled module and return if so.
  423.     
  424.     set dirName [string tolower \
  425.         [lindex [set elements \
  426.         [ ::apacheparserutils::getElements $data ]] 0]]
  427.  
  428.     if [string match serverroot $dirName] {
  429.     
  430.     # join is necessary to handle spaces on Windows
  431.     
  432.     set includeroot [join [lindex $elements 1]]
  433.     }       
  434.  
  435.     if ![$moduleManager isDirectiveEnabled $dirName] {
  436.     return {}
  437.     }
  438.  
  439.     if [ isSpecialCase $dirName ] {   
  440.  
  441.     set xuiDirectiveName [string tolower $specialCaseDirectiveMapping($dirName)]
  442.  
  443.     # check if currentXMLDirectives contains xuiDirective associated
  444.     # with this special case
  445.  
  446.     if [llength [set xuiDirective [ getXmlDirectivesWithThatName $xuiDirectiveName ]]] {
  447.  
  448.         # yes -> process it append to result
  449.         #        delete from currentXml
  450.         
  451.         set result [dumpSpecialCase $xuiDirectiveName $xuiDirective] 
  452.         set idx [lsearch -exact $currentXmlDirectives $xuiDirective]
  453.         set currentXmlDirectives [lreplace $currentXmlDirectives $idx $idx]         
  454.         return $result
  455.     } else {
  456.  
  457.         # no -> We already processed it return nothing
  458.  
  459.         return {}
  460.     }
  461.  
  462.     }
  463.  
  464.     if [llength [set list [ getXmlDirectivesWithThatName $dirName ]]] {
  465.     
  466.     # yes --> process it append to result
  467.     #         delete from currentXmlDirectives
  468.  
  469.     # switch depending if unknown or not
  470.  
  471.     foreach one $list {
  472.         if [$one doYouBelongTo unknownDirective] {
  473.         append result [$one getValue]\n
  474.         } else {
  475.         append result [dumpDirective $one]
  476.         }
  477.         set idx [lsearch -exact $currentXmlDirectives $one]
  478.         set currentXmlDirectives [lreplace $currentXmlDirectives $idx $idx]         
  479.     }
  480.     return $result
  481.     }
  482.     
  483.     
  484.     # If we are here it was not found, so we ignore it
  485.  
  486.     return {}
  487.  
  488. }
  489.  
  490. body apachePrettyDumper::getXmlDirectivesWithThatName {dirName} {
  491.     set result {}
  492.     foreach one $currentXmlDirectives {
  493.     if ![string compare [string tolower [$one getName]] $dirName] {
  494.         lappend result $one
  495.     }
  496.     }
  497.     return $result
  498. }
  499.  
  500. body apachePrettyDumper::dumpDirective {directive} {
  501.     set result {}
  502.     if [$directive doYouBelongTo unknownDirective] {
  503.     set result "[$directive getValue]\n"
  504.     return $result
  505.     }
  506.     set dirName [string tolower [$directive getName]]
  507.     switch [$directive getXuiClass] {
  508.     string - number  {
  509.         set value [$directive getValue]
  510.         if [string compare $value [$directive getDefault]] {
  511.         set value [$directive getValue]
  512.         if {[$directive doYouBelongTo file] || \
  513.             [$directive doYouBelongTo directory] } {
  514.             if [regexp {\ } $value] {
  515.             set value "\"$value\""
  516.             }
  517.         }
  518.         append result "$dirName $value\n"
  519.         }
  520.     } boolean {
  521.         set value [$directive getValue]
  522.         if [string compare $value [$directive getDefault]] {
  523.         switch $value {
  524.             0 {
  525.             append result "$dirName off\n"
  526.             } 1 {
  527.             append result "$dirName on\n"
  528.             }
  529.         }
  530.         }
  531.     } choice {
  532.  
  533.         # TO-DO: Check if it is multiple choice
  534.  
  535.         if ![string match [$directive getName] [$directive getDefault]] {
  536.         append result \
  537.             "$dirName [$directive getSelected]\n"     
  538.         }
  539.     } default {
  540.         error "No special case and not recognized in dumping\
  541.             [$directive getXuiClass] [$directive getName]"
  542.     }
  543.     }
  544.     if ![string length [string trim $result]] {
  545.     return {}
  546.     } else {
  547.     return $result
  548.     }    
  549. }
  550.                    
  551.  
  552. body apachePrettyDumper::dumpSpecialCase {dirName directive} {
  553.     set result [$specialCaseMapping($dirName) $directive] 
  554.     if ![string length [string trim $result]] {
  555.     return {}
  556.     } else {
  557.     return $result
  558.     }
  559. }
  560.  
  561. body apachePrettyDumper::isSpecialCase {dirName} {
  562.     return [info exists specialCaseDirectiveMapping($dirName)]  
  563. }
  564.  
  565. body apachePrettyDumper::setSpecialCase { procedure args } {
  566.  
  567.     # Maps xuiObjects -> procedures to dump them
  568.  
  569.     foreach xuiDirectiveName $args {
  570.     set specialCaseMapping([string tolower $xuiDirectiveName]) \
  571.         $procedure
  572.     }
  573. }
  574.  
  575. # Sets mapping between directives that are found in httpd.conf and their corresponding 
  576. # xuiObject directive. Example, allow and deny httpd.conf directives map to access directive
  577.  
  578. body apachePrettyDumper::setSpecialCaseDirectiveMapping \
  579.     { xuiDirectiveName args } {
  580.     foreach one $args {
  581.     set specialCaseDirectiveMapping([string tolower $one]) \
  582.         $xuiDirectiveName
  583.     }
  584.  
  585. }
  586.  
  587.       
  588.