db.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. # -*- coding: utf-8 -*-
  2. #
  3. # cms.py - simple WSGI/Python based CMS script
  4. #
  5. # Copyright (C) 2011-2024 Michael Buesch <m@bues.ch>
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #from cms.cython_support cimport * #@cy
  20. from cms.exception import *
  21. from cms.pageident import *
  22. from cms.util import fs, datetime #+cimport
  23. from cms.socket import *
  24. import re
  25. import sys
  26. import importlib.machinery
  27. __all__ = [
  28. "CMSDatabase",
  29. ]
  30. class CMSDatabase(object):
  31. validate = CMSPageIdent.validateName
  32. def __init__(self, rundir):
  33. try:
  34. self.dbsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  35. self.dbsock.connect(str(rundir / "cms-fsd.sock"))
  36. except Exception:
  37. raise CMSException(500, "cms-fsd communication error")
  38. try:
  39. self.postsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  40. self.postsock.connect(str(rundir / "cms-postd.sock"))
  41. except Exception:
  42. raise CMSException(500, "cms-postd communication error")
  43. def __communicateDb(self, msg):
  44. try:
  45. self.dbsock.sendall(msg.pack())
  46. return recv_message(self.dbsock, MAGIC_DB)
  47. except Exception:
  48. raise CMSException(500, "cms-fsd communication error")
  49. def __communicatePost(self, msg):
  50. try:
  51. self.postsock.sendall(msg.pack())
  52. return recv_message(self.postsock, MAGIC_POST)
  53. except Exception:
  54. raise CMSException(500, "cms-postd communication error")
  55. @staticmethod
  56. def __encode(s):
  57. try:
  58. if s is not None:
  59. return s.encode("UTF-8", "strict")
  60. except UnicodeError:
  61. pass
  62. return b""
  63. @staticmethod
  64. def __decode(b):
  65. try:
  66. if b is not None:
  67. return b.decode("UTF-8", "strict")
  68. except UnicodeError:
  69. pass
  70. return ""
  71. def getNavStop(self, pageIdent):
  72. reply = self.__communicateDb(MsgGetPage(
  73. path=pageIdent.getFilesystemPath(),
  74. get_title=False,
  75. get_data=False,
  76. get_stamp=False,
  77. get_prio=False,
  78. get_redirect=False,
  79. get_nav_stop=True,
  80. get_nav_label=False,
  81. ))
  82. nav_stop = bool(reply.nav_stop)
  83. return nav_stop
  84. def getHeaders(self, pageIdent):
  85. reply = self.__communicateDb(MsgGetHeaders(
  86. path=pageIdent.getFilesystemPath(),
  87. ))
  88. assert_is_msg(reply, MsgHeaders)
  89. data = self.__decode(reply.data)
  90. return data
  91. def getPage(self, pageIdent):
  92. reply = self.__communicateDb(MsgGetPage(
  93. path=pageIdent.getFilesystemPath(),
  94. get_title=True,
  95. get_data=True,
  96. get_stamp=True,
  97. get_prio=False,
  98. get_redirect=True,
  99. get_nav_stop=False,
  100. get_nav_label=False,
  101. ))
  102. assert_is_msg(reply, MsgPage)
  103. redirect = self.__decode(reply.redirect).strip()
  104. if redirect:
  105. raise CMSException301(redirect)
  106. title = self.__decode(reply.title)
  107. data = self.__decode(reply.data)
  108. stamp = datetime.utcfromtimestamp(reply.stamp or 0)
  109. return (title, data, stamp)
  110. def getPageTitle(self, pageIdent):
  111. reply = self.__communicateDb(MsgGetPage(
  112. path=pageIdent.getFilesystemPath(),
  113. get_title=True,
  114. get_data=False,
  115. get_stamp=False,
  116. get_prio=False,
  117. get_redirect=False,
  118. get_nav_stop=False,
  119. get_nav_label=False,
  120. ))
  121. assert_is_msg(reply, MsgPage)
  122. title = self.__decode(reply.title)
  123. return title
  124. def getPageStamp(self, pageIdent):
  125. reply = self.__communicateDb(MsgGetPage(
  126. path=pageIdent.getFilesystemPath(),
  127. get_title=False,
  128. get_data=False,
  129. get_stamp=True,
  130. get_prio=False,
  131. get_redirect=False,
  132. get_nav_stop=False,
  133. get_nav_label=False,
  134. ))
  135. assert_is_msg(reply, MsgPage)
  136. stamp = datetime.utcfromtimestamp(reply.stamp or 0)
  137. return stamp
  138. # Get a list of sub-pages.
  139. # Returns list of (pagename, navlabel, prio)
  140. def getSubPages(self, pageIdent, sortByPrio=True):
  141. reply = self.__communicateDb(MsgGetSubPages(
  142. path=pageIdent.getFilesystemPath(),
  143. ))
  144. assert_is_msg(reply, MsgSubPages)
  145. res = []
  146. for i in range(len(reply.pages)):
  147. pagename = self.__decode(reply.pages[i])
  148. navlabel = self.__decode(reply.nav_labels[i]).strip()
  149. prio = reply.prios[i]
  150. res.append( (pagename, navlabel, prio) )
  151. if sortByPrio:
  152. res.sort(key = lambda e: "%010d_%s" % (e[2], e[1]))
  153. return res
  154. # Get the contents of a @MACRO().
  155. def getMacro(self, macroname, pageIdent=None):
  156. reply = self.__communicateDb(MsgGetMacro(
  157. parent=pageIdent.getFilesystemPath() if pageIdent is not None else "",
  158. name=macroname,
  159. ))
  160. assert_is_msg(reply, MsgMacro)
  161. data = self.__decode(reply.data)
  162. return '\n'.join( l for l in data.splitlines() if l )
  163. def getString(self, name, default=None):
  164. reply = self.__communicateDb(MsgGetString(
  165. name=name,
  166. ))
  167. assert_is_msg(reply, MsgString)
  168. string = self.__decode(reply.data).strip()
  169. if string:
  170. return string
  171. return default or ""
  172. def getImage(self, name):
  173. name = CMSPageIdent.validateSafePathComponent(name)
  174. reply = self.__communicateDb(MsgGetImage(
  175. name=name,
  176. ))
  177. assert_is_msg(reply, MsgImage)
  178. return reply.data
  179. def runPostHandler(self, pageIdent, formFields, query):
  180. reply = self.__communicatePost(MsgRunPostHandler(
  181. path=pageIdent.getFilesystemPath() + "/post.py",
  182. query=query.items(),
  183. form_fields=formFields.items(),
  184. ))
  185. assert_is_msg(reply, MsgPostHandlerResult)
  186. if reply.error:
  187. raise CMSException(400, reply.error)
  188. return bytes(reply.body), reply.mime