Browse Source

记录pod文件相关修改

Steven 3 days ago
parent
commit
82367eeef8
100 changed files with 33572 additions and 28 deletions
  1. 6 4
      KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Controller/MusicRoomViewController.m
  2. 1 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Model/Records.h
  3. 8 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Model/Records.m
  4. 7 4
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/Controller/MusicRoomHomeworkStudentController.m
  5. 1 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/Model/HomeworkListModel.h
  6. 6 4
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/View/HomeworkBodyView.m
  7. 1 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/Model/AccompanyLessonModel.h
  8. 10 0
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/Model/AccompanyLessonModel.m
  9. 7 5
      KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/View/MyLessonBodyView.m
  10. 5 5
      KulexiuForTeacher/KulexiuForTeacher/Module/Mine/Setting/DeleteAccount/View/KSDeleteAccountTipsAlert.xib
  11. 5 1
      KulexiuForTeacher/KulexiuForTeacher/Module/Mine/Setting/DeleteAccount/View/KSDeleteFailedAlertView.m
  12. 5 5
      KulexiuForTeacher/Podfile.lock
  13. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/alipay_msp_back@2x.png
  14. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/alipay_msp_refresh@2x.png
  15. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/bar@2x.png
  16. 1 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/bridge.js
  17. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/refresh@2x.png
  18. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/refresh_click@2x.png
  19. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/shutdown@2x.png
  20. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/shutdown_click@2x.png
  21. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/AlipaySDK
  22. BIN
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/AlipaySDK-inside-Info.plist
  23. 56 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AFServiceCenter.h
  24. 43 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AFServiceResponse.h
  25. 33 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/APayAuthInfo.h
  26. 223 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AlipaySDK.h
  27. 21 0
      KulexiuForTeacher/Pods/AlipaySDK-iOS/LICENSE
  28. 35 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/LICENSE.txt
  29. 121 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/README.markdown
  30. 1226 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h
  31. 8526 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m
  32. 1036 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h
  33. 5632 0
      KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m
  34. 14 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDData.h
  35. 158 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDData.m
  36. 12 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDNumber.h
  37. 88 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDNumber.m
  38. 56 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDRange.h
  39. 104 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDRange.m
  40. 45 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h
  41. 195 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m
  42. 119 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPConnection.h
  43. 2708 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPConnection.m
  44. 136 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPLogging.h
  45. 48 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPMessage.h
  46. 113 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPMessage.m
  47. 149 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPResponse.h
  48. 205 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPServer.h
  49. 772 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPServer.m
  50. 65 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h
  51. 529 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.m
  52. 33 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h
  53. 86 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m
  54. 23 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h
  55. 211 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m
  56. 75 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h
  57. 405 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.m
  58. 13 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h
  59. 79 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m
  60. 52 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h
  61. 292 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.m
  62. 9 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h
  63. 38 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m
  64. 25 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h
  65. 237 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m
  66. 12 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h
  67. 73 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m
  68. 105 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/WebSocket.h
  69. 791 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Core/WebSocket.m
  70. 7 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.h
  71. 160 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.m
  72. 11 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.h
  73. 372 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.m
  74. 7 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.h
  75. 49 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.m
  76. 8 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.h
  77. 69 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.m
  78. 18 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/LICENSE.txt
  79. 27 0
      KulexiuForTeacher/Pods/CocoaHTTPServer/README.markdown
  80. 638 0
      KulexiuForTeacher/Pods/CocoaLumberjack/CHANGELOG.md
  81. 14 0
      KulexiuForTeacher/Pods/CocoaLumberjack/LICENSE
  82. 301 0
      KulexiuForTeacher/Pods/CocoaLumberjack/README.md
  83. 57 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m
  84. 205 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m
  85. 133 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m
  86. 636 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m
  87. 31 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h
  88. 1865 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m
  89. 1321 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m
  90. 21 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m
  91. 158 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m
  92. 1446 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m
  93. 57 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m
  94. 185 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m
  95. 240 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m
  96. 202 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m
  97. 110 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m
  98. 30 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/PrivacyInfo.xcprivacy
  99. 104 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h
  100. 0 0
      KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h

+ 6 - 4
KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Controller/MusicRoomViewController.m

@@ -164,10 +164,12 @@
             return;
         }
         Records *studentModel = self.studentArray[indexPath.row];
-        HomeworkDetailViewController *detailCtrl = [[HomeworkDetailViewController alloc] init];
-        detailCtrl.courseId = self.detailModel.courseId;
-        detailCtrl.studentId = studentModel.studentId;
-        [self.navigationController pushViewController:detailCtrl animated:YES];
+        if (studentModel.delFlag != YES) {
+            HomeworkDetailViewController *detailCtrl = [[HomeworkDetailViewController alloc] init];
+            detailCtrl.courseId = self.detailModel.courseId;
+            detailCtrl.studentId = studentModel.studentId;
+            [self.navigationController pushViewController:detailCtrl animated:YES];
+        }
     }
 }
 

+ 1 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Model/Records.h

@@ -17,6 +17,7 @@
 @property (nonatomic, strong) NSString *submitHomework;
 @property (nonatomic, strong) NSString *studentSubject;
 @property (nonatomic, strong) NSString *homeworkStatus;
+@property (nonatomic, assign) BOOL delFlag; // 是否注销
 
 + (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
 - (instancetype)initWithDictionary:(NSDictionary *)dict;

+ 8 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Course/MusicRoom/Model/Records.m

@@ -14,6 +14,7 @@ NSString *const kRecordsStudentAvatar = @"studentAvatar";
 NSString *const kRecordsSubmitHomework = @"submitHomework";
 NSString *const kRecordsSubmitStudentSubject = @"subjectName";
 NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
+NSString *const kRecordsDelFlag = @"delFlag";
 
 @interface Records ()
 
@@ -29,6 +30,7 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
 @synthesize submitHomework = _submitHomework;
 @synthesize studentSubject = _studentSubject;
 @synthesize homeworkStatus = _homeworkStatus;
+@synthesize delFlag = _delFlag;
 
 + (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
 {
@@ -48,6 +50,7 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
             self.submitHomework = [self objectOrNilForKey:kRecordsSubmitHomework fromDictionary:dict];
             self.studentSubject = [self objectOrNilForKey:kRecordsSubmitStudentSubject fromDictionary:dict];
             self.homeworkStatus = [self objectOrNilForKey:kRecordsHomeworkStatus fromDictionary:dict];
+        self.delFlag = [[self objectOrNilForKey:kRecordsDelFlag fromDictionary:dict] boolValue];
     }
     
     return self;
@@ -63,6 +66,7 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
     [mutableDict setValue:self.submitHomework forKey:kRecordsSubmitHomework];
     [mutableDict setValue:self.studentSubject forKey:kRecordsSubmitStudentSubject];
     [mutableDict setValue:self.homeworkStatus forKey:kRecordsHomeworkStatus];
+    [mutableDict setValue:[NSNumber numberWithBool:self.delFlag] forKey:kRecordsDelFlag];
     return [NSDictionary dictionaryWithDictionary:mutableDict];
 }
 
@@ -95,6 +99,8 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
     self.submitHomework = [aDecoder decodeObjectForKey:kRecordsSubmitHomework];
     self.studentSubject = [aDecoder decodeObjectForKey:kRecordsSubmitStudentSubject];
     self.homeworkStatus = [aDecoder decodeObjectForKey:kRecordsHomeworkStatus];
+    self.delFlag = [aDecoder decodeBoolForKey:kRecordsDelFlag];
+    
     return self;
 }
 
@@ -107,6 +113,7 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
     [aCoder encodeObject:_submitHomework forKey:kRecordsSubmitHomework];
     [aCoder encodeObject:_studentSubject forKey:kRecordsSubmitStudentSubject];
     [aCoder encodeObject:_homeworkStatus forKey:kRecordsHomeworkStatus];
+    [aCoder encodeBool:_delFlag forKey:kRecordsDelFlag];
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -121,6 +128,7 @@ NSString *const kRecordsHomeworkStatus = @"homeworkStatus";
         copy.submitHomework = [self.submitHomework copyWithZone:zone];
         copy.studentSubject = [self.studentSubject copyWithZone:zone];
         copy.homeworkStatus = [self.homeworkStatus copyWithZone:zone];
+        copy.delFlag = self.delFlag;
     }
     
     return copy;

+ 7 - 4
KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/Controller/MusicRoomHomeworkStudentController.m

@@ -76,10 +76,13 @@
 }
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
     Records *studentModel = self.studentArray[indexPath.row];
-    HomeworkDetailViewController *detailCtrl = [[HomeworkDetailViewController alloc] init];
-    detailCtrl.courseId = self.detailModel.courseId;
-    detailCtrl.studentId = studentModel.studentId;
-    [self.navigationController pushViewController:detailCtrl animated:YES];
+    // 如果学生注销,不进入详情
+    if (studentModel.delFlag != YES) {
+        HomeworkDetailViewController *detailCtrl = [[HomeworkDetailViewController alloc] init];
+        detailCtrl.courseId = self.detailModel.courseId;
+        detailCtrl.studentId = studentModel.studentId;
+        [self.navigationController pushViewController:detailCtrl animated:YES];
+    }
 }
 
 - (UITableView *)tableView {

+ 1 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/Model/HomeworkListModel.h

@@ -34,6 +34,7 @@
 @property (nonatomic, strong) NSString *homeworkStatus;
 @property (nonatomic, strong) NSString *imGroupId;
 @property (nonatomic, strong) NSString *imUserId;
+@property (nonatomic, assign) BOOL delFlag;
 
 + (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
 - (instancetype)initWithDictionary:(NSDictionary *)dict;

+ 6 - 4
KulexiuForTeacher/KulexiuForTeacher/Module/Home/Homework/View/HomeworkBodyView.m

@@ -229,10 +229,12 @@
         [self.naviController pushViewController:ctrl animated:YES];
     }
     else {
-        HomeworkDetailViewController *detailVC = [[HomeworkDetailViewController alloc] init];
-        detailVC.courseId = model.courseId;
-        detailVC.studentId = model.studentId;
-        [self.naviController pushViewController:detailVC animated:YES];
+        if (model.delFlag != YES) {
+            HomeworkDetailViewController *detailVC = [[HomeworkDetailViewController alloc] init];
+            detailVC.courseId = model.courseId;
+            detailVC.studentId = model.studentId;
+            [self.naviController pushViewController:detailVC animated:YES];
+        }
     }
     
 }

+ 1 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/Model/AccompanyLessonModel.h

@@ -26,6 +26,7 @@
 @property (nonatomic, strong) NSString *status;
 @property (nonatomic, strong) NSString *teacherReplied;
 @property (nonatomic, strong) NSString *imUserId;
+@property (nonatomic, assign) BOOL delFlag;
 
 + (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
 - (instancetype)initWithDictionary:(NSDictionary *)dict;

+ 10 - 0
KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/Model/AccompanyLessonModel.m

@@ -23,6 +23,10 @@ NSString *const kAccompanyLessonModelStartTime = @"startTime";
 NSString *const kAccompanyLessonModelStatus = @"status";
 NSString *const kAccompanyLessonModelTeacherReplied = @"teacherReplied";
 NSString *const kAccompanyLessonModelImUserId = @"imUserId";
+NSString *const kAccompanyLessonModelDelFlag = @"delFlag";
+
+
+
 
 @interface AccompanyLessonModel ()
 
@@ -47,6 +51,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
 @synthesize status = _status;
 @synthesize teacherReplied = _teacherReplied;
 @synthesize imUserId = _imUserId;
+@synthesize delFlag = _delFlag;
 
 + (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
 {
@@ -75,6 +80,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
             self.status = [self objectOrNilForKey:kAccompanyLessonModelStatus fromDictionary:dict];
             self.teacherReplied = [self objectOrNilForKey:kAccompanyLessonModelTeacherReplied fromDictionary:dict];
             self.imUserId = [self objectOrNilForKey:kAccompanyLessonModelImUserId fromDictionary:dict];
+        self.delFlag = [self objectOrNilForKey:kAccompanyLessonModelDelFlag fromDictionary:dict];
     }
     
     return self;
@@ -99,6 +105,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
     [mutableDict setValue:self.status forKey:kAccompanyLessonModelStatus];
     [mutableDict setValue:self.teacherReplied forKey:kAccompanyLessonModelTeacherReplied];
     [mutableDict setValue:self.imUserId forKey:kAccompanyLessonModelImUserId];
+    [mutableDict setValue:[NSNumber numberWithBool:self.delFlag] forKey:kAccompanyLessonModelDelFlag];
     
     return [NSDictionary dictionaryWithDictionary:mutableDict];
 }
@@ -141,6 +148,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
     self.status = [aDecoder decodeObjectForKey:kAccompanyLessonModelStatus];
     self.teacherReplied = [aDecoder decodeObjectForKey:kAccompanyLessonModelTeacherReplied];
     self.imUserId = [aDecoder decodeObjectForKey:kAccompanyLessonModelImUserId];
+    self.delFlag = [aDecoder decodeBoolForKey:kAccompanyLessonModelDelFlag];
     return self;
 }
 
@@ -162,6 +170,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
     [aCoder encodeObject:_status forKey:kAccompanyLessonModelStatus];
     [aCoder encodeObject:_teacherReplied forKey:kAccompanyLessonModelTeacherReplied];
     [aCoder encodeObject:_imUserId forKey:kAccompanyLessonModelImUserId];
+    [aCoder encodeBool:_delFlag forKey:kAccompanyLessonModelDelFlag];
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -185,6 +194,7 @@ NSString *const kAccompanyLessonModelImUserId = @"imUserId";
         copy.status = [self.status copyWithZone:zone];
         copy.teacherReplied = [self.teacherReplied copyWithZone:zone];
         copy.imUserId = [self.imUserId copyWithZone:zone];
+        copy.delFlag = self.delFlag;
     }
     
     return copy;

+ 7 - 5
KulexiuForTeacher/KulexiuForTeacher/Module/Home/MyCourse/View/MyLessonBodyView.m

@@ -447,11 +447,13 @@
 }
 
 - (void)showAccompanyDetail:(AccompanyLessonModel *)model {
-    AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
-    detailVC.courseId = model.courseId;
-    detailVC.courseGroupId = model.courseGoupId;
-    detailVC.studentId = model.userId;
-    [self.naviController pushViewController:detailVC animated:YES];
+    if (model.delFlag != YES) {
+        AccompanyDetailViewController *detailVC = [[AccompanyDetailViewController alloc] init];
+        detailVC.courseId = model.courseId;
+        detailVC.courseGroupId = model.courseGoupId;
+        detailVC.studentId = model.userId;
+        [self.naviController pushViewController:detailVC animated:YES];
+    }
 }
 
 - (void)showAdjustPicker:(AccompanyLessonModel *)model {

+ 5 - 5
KulexiuForTeacher/KulexiuForTeacher/Module/Mine/Setting/DeleteAccount/View/KSDeleteAccountTipsAlert.xib

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
     <device id="retina6_12" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
@@ -17,7 +17,7 @@
                     <rect key="frame" x="44" y="325" width="305" height="202"/>
                     <subviews>
                         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="注销账号" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uBu-35-0tK">
-                            <rect key="frame" x="115.66666666666666" y="20" width="74" height="25"/>
+                            <rect key="frame" x="116.66666666666666" y="20" width="71.666666666666657" height="25"/>
                             <constraints>
                                 <constraint firstAttribute="height" constant="25" id="RB2-30-oM5"/>
                             </constraints>
@@ -89,7 +89,7 @@
                             </connections>
                         </button>
                         <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7ot-5U-toN">
-                            <rect key="frame" x="157.66666666666666" y="147" width="127.33333333333334" height="40"/>
+                            <rect key="frame" x="157.66666666666666" y="142" width="127.33333333333334" height="40"/>
                             <constraints>
                                 <constraint firstAttribute="height" constant="40" id="yIS-km-gU5"/>
                             </constraints>
@@ -121,9 +121,9 @@
                         <constraint firstItem="uBu-35-0tK" firstAttribute="top" secondItem="br2-3O-aM2" secondAttribute="top" constant="20" id="EXn-Dv-PHZ"/>
                         <constraint firstItem="z8j-hU-m67" firstAttribute="leading" secondItem="br2-3O-aM2" secondAttribute="leading" constant="20" id="Oy2-kb-dCJ"/>
                         <constraint firstItem="7ot-5U-toN" firstAttribute="width" secondItem="Yzz-5W-rzh" secondAttribute="width" id="Wcb-cE-J8P"/>
-                        <constraint firstAttribute="bottom" secondItem="7ot-5U-toN" secondAttribute="bottom" constant="15" id="X2K-Kv-cH2"/>
                         <constraint firstAttribute="height" constant="202" id="aH2-gc-0BK"/>
                         <constraint firstItem="uBu-35-0tK" firstAttribute="centerX" secondItem="br2-3O-aM2" secondAttribute="centerX" id="egU-Fm-DjU"/>
+                        <constraint firstItem="7ot-5U-toN" firstAttribute="bottom" secondItem="Yzz-5W-rzh" secondAttribute="bottom" id="n5s-FS-C4i"/>
                         <constraint firstAttribute="trailing" secondItem="7ot-5U-toN" secondAttribute="trailing" constant="20" id="nx1-G3-l56"/>
                         <constraint firstItem="Yzz-5W-rzh" firstAttribute="leading" secondItem="br2-3O-aM2" secondAttribute="leading" constant="15" id="qVr-nV-bMx"/>
                         <constraint firstItem="7ot-5U-toN" firstAttribute="leading" secondItem="Yzz-5W-rzh" secondAttribute="trailing" constant="15" id="s6U-WW-fME"/>

+ 5 - 1
KulexiuForTeacher/KulexiuForTeacher/Module/Mine/Setting/DeleteAccount/View/KSDeleteFailedAlertView.m

@@ -174,7 +174,11 @@
 }
 
 - (void)showAlert {
-    [[NSObject getKeyWindow] addSubview:self];
+    UIWindow *window = [NSObject getKeyWindow];
+    [window addSubview:self];
+    [self mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.bottom.mas_equalTo(window);
+    }];    
 }
 
 - (void)removeAlert {

+ 5 - 5
KulexiuForTeacher/Podfile.lock

@@ -36,9 +36,9 @@ PODS:
   - MJExtension (3.3.0)
   - MJRefresh (3.1.17)
   - NTLBridge (3.1.4)
-  - QCloudCore/WithoutMTA (6.2.8)
-  - QCloudCOSXML/Transfer (6.2.8):
-    - QCloudCore/WithoutMTA (= 6.2.8)
+  - QCloudCore/WithoutMTA (6.3.7)
+  - QCloudCOSXML/Transfer (6.3.7):
+    - QCloudCore/WithoutMTA (= 6.3.7)
   - Reachability (3.2)
   - ReactiveObjC (3.1.1)
   - RSKImageCropper (3.0.2)
@@ -352,8 +352,8 @@ SPEC CHECKSUMS:
   MJExtension: 01704cca2b60a214c10761b6491eab74069d68a9
   MJRefresh: ee5b68f639775462faba4db0fd243baf4d42c2cf
   NTLBridge: 49780dc966976d3221a0eb03c7368617c1987cb6
-  QCloudCore: 7d480d45acc287a10caf1bb5b11f32e9fb2bb8b2
-  QCloudCOSXML: d7c1f08486ae540ef7a07153528946321e44993b
+  QCloudCore: 1316c60dbc3c308d7d90d3406a4a735e43b03c42
+  QCloudCOSXML: 2f4c36887311276014e7c9b70ea235ea29f975d8
   Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
   ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
   RSKImageCropper: 1ac71e9a82e3f41eea3eedfff8eacb0d3821c9ec

BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/alipay_msp_back@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/alipay_msp_refresh@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/bar@2x.png


File diff suppressed because it is too large
+ 1 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/bridge.js


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/refresh@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/refresh_click@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/shutdown@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.bundle/shutdown_click@2x.png


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/AlipaySDK


BIN
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/AlipaySDK-inside-Info.plist


+ 56 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AFServiceCenter.h

@@ -0,0 +1,56 @@
+//
+//  AFServiceCenter.h
+//  AFServiceSDK
+//
+//  Created by jiajunchen on 02/01/2018.
+//  Copyright © 2018 antfin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class AFServiceResponse;
+
+/**
+ SDK支持的业务枚举值
+
+ - AFServiceEInvoice: 电子发票
+ - AFServiceAuth: 账户授权
+ */
+typedef NS_ENUM(NSUInteger, AFService) {
+    AFServiceEInvoice,
+    AFServiceAuth,
+    AFServiceDeduct
+};
+
+
+extern NSString * const kAFServiceOptionBizParams;      // 钱包服务调用入参
+extern NSString * const kAFServiceOptionCallbackScheme; // 业务回跳当前app的scheme
+extern NSString * const kAFServiceOptionNotUseLanding;  // 不使用支付宝提示下载页做补偿,为true时需要商户自己处理用户未安装支付宝的情况
+extern NSString * const kAFServiceBizParamsKeyUrl;      // 独立签约入参url
+
+typedef void(^AFServiceResultBlock)(AFServiceResponse *response);
+
+@interface AFServiceCenter : NSObject
+
+/**
+ 调用钱包服务
+
+ @param service 业务service, 见AFService枚举值
+ @param params  参数Dictionary, key值详情参见kAFServiceOptionBizParams、kAFServiceOptionCallbackScheme注释
+ @param block   业务结果回调的block, block参数是AFServiceResponse类型,业务结果通过result属性获取,如果未用户未安装支付宝并且kAFServiceOptionNotUseLanding未设置为true,会使用H5landing页做补偿,这种情况下不会有block回调结果。
+ */
++ (void)callService:(AFService)service
+         withParams:(NSDictionary *)params
+      andCompletion:(AFServiceResultBlock)block;
+
+
+/**
+ 处理钱包服务回跳APP的URL
+
+ @param url 回跳URL
+ @param block 业务结果回掉的block,详情见调用接口入参上的block。注意此接口上的block只有在跳转钱包后,当前APP被系统回收的情况下回跳才生效
+ */
++ (void)handleResponseURL:(NSURL *)url
+           withCompletion:(AFServiceResultBlock)block;
+
+@end

+ 43 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AFServiceResponse.h

@@ -0,0 +1,43 @@
+//
+//  AFServiceResponse.h
+//  AFServiceSDK
+//
+//  Created by jiajunchen on 08/01/2018.
+//  Copyright © 2018 antfin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/**
+ 钱包服务调用结果状态吗
+
+ - AFResSuccess: 默认值,业务调用成功,结果数据参见result字段
+ - AFResInvalidService: service枚举值错误
+ - AFResInvalidURL: 钱包回跳URL错误
+ - AFResRepeatCall: 业务重复调用(3s内)
+ - AFResOpenURLErr: 跳转失败
+ */
+typedef NS_ENUM(NSUInteger, AFResCode) {
+    AFResSuccess = 0,
+    AFResInvalidService = 100,
+    AFResInvalidURL,
+    AFResRepeatCall,
+    AFResOpenURLErr,
+};
+
+
+@interface AFServiceResponse : NSObject
+
+
+/**
+ 业务调用状态吗
+ */
+@property (nonatomic, assign) AFResCode responseCode;
+
+
+/**
+ 业务结果Dictionary, 内容请参考具体业务方接入文档
+ */
+@property (readonly) NSDictionary *result;
+
+@end

+ 33 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/APayAuthInfo.h

@@ -0,0 +1,33 @@
+//
+//  APAuthInfo.h
+//  APAuth
+//
+//  Created by antfin on 17-10-24.
+//  Copyright (c) 2017年 AntFin. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface APayAuthInfo : NSObject
+
+@property(nonatomic, copy)NSString *appID;
+@property(nonatomic, copy)NSString *pid;
+@property(nonatomic, copy)NSString *redirectUri;
+
+/**
+ *  初始化AuthInfo
+ *
+ *  @param appIDStr     应用ID
+ *  @param pidStr       商户ID   可不填
+ *  @param uriStr       授权的应用回调地址  比如:alidemo://auth
+ *
+ *  @return authinfo实例
+ */
+- (id)initWithAppID:(NSString *)appIDStr
+                pid:(NSString *)pidStr
+        redirectUri:(NSString *)uriStr;
+
+- (NSString *)description;
+- (NSString *)wapDescription;
+
+@end

+ 223 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/AlipaySDK.framework/Headers/AlipaySDK.h

@@ -0,0 +1,223 @@
+//
+//  AlipaySDK.h
+//  AlipaySDK
+//
+//  Created by antfin on 17-10-24.
+//  Copyright (c) 2017年 AntFin. All rights reserved.
+//
+
+
+////////////////////////////////////////////////////////
+///////////////// 支付宝标准版本支付SDK ///////////////////
+///////// version:15.8.08  motify:2022.02.15///////////
+////////////////////////////////////////////////////////
+
+#import <UIKit/UIKit.h>
+#import "APayAuthInfo.h"
+#import "AFServiceCenter.h"
+#import "AFServiceResponse.h"
+
+typedef void(^CompletionBlock)(NSDictionary *resultDic);
+
+typedef enum {
+    ALIPAY_TIDFACTOR_IMEI,
+    ALIPAY_TIDFACTOR_IMSI,
+    ALIPAY_TIDFACTOR_TID,
+    ALIPAY_TIDFACTOR_CLIENTKEY,
+    ALIPAY_TIDFACTOR_VIMEI,
+    ALIPAY_TIDFACTOR_VIMSI,
+    ALIPAY_TIDFACTOR_CLIENTID,
+    ALIPAY_TIDFACTOR_APDID,
+    ALIPAY_TIDFACTOR_MAX
+} AlipayTidFactor;
+
+@interface AlipaySDK : NSObject
+
+/**
+ *  创建支付单例服务
+ *
+ *  @return 返回单例对象
+ */
++ (AlipaySDK *)defaultService;
+
+/**
+ *  用于设置SDK使用的window,如果没有自行创建window无需设置此接口
+ */
+@property (nonatomic, weak) UIWindow *targetWindow;
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝支付相关接口/////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  支付接口
+ *
+ *  @param orderStr        支付订单信息字串
+ *  @param schemeStr       调用支付的app注册在info.plist中的scheme
+ *  @param completionBlock 支付结果回调Block,用于wap支付结果回调
+                           跳转支付宝支付时只有当processOrderWithPaymentResult接口的completionBlock为nil时会使用这个bolock
+ */
+- (void)payOrder:(NSString *)orderStr
+      fromScheme:(NSString *)schemeStr
+        callback:(CompletionBlock)completionBlock;
+
+/**
+ *  支付接口 v2
+ *
+ *  @param orderStr        支付订单信息字串
+ *  @param dynamicLaunch   是否使用动态配置策略跳转支付宝支付
+ *  @param schemeStr       调用支付的app注册在info.plist中的scheme
+ *  @param completionBlock 支付结果回调Block,用于wap支付结果回调
+ 跳转支付宝支付时只有当processOrderWithPaymentResult接口的completionBlock为nil时会使用这个bolock
+ */
+- (void)payOrder:(NSString *)orderStr
+   dynamicLaunch:(BOOL)dynamicLaunch
+      fromScheme:(NSString *)schemeStr
+        callback:(CompletionBlock)completionBlock;
+
+/**
+ *  处理支付宝app支付后跳回商户app携带的支付结果Url
+ *
+ *  @param resultUrl        支付宝app返回的支付结果url
+ *  @param completionBlock  支付结果回调 为nil时默认使用支付接口的completionBlock
+ */
+- (void)processOrderWithPaymentResult:(NSURL *)resultUrl
+                      standbyCallback:(CompletionBlock)completionBlock;
+
+/**
+ *  获取交易token。
+ *
+ *  @return 交易token,若无则为空。
+ */
+- (NSString *)fetchTradeToken;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝授权 2.0 相关接口////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  快登授权2.0
+ *
+ *  @param infoStr          授权请求信息字串
+ *  @param schemeStr        调用授权的app注册在info.plist中的scheme
+ *  @param completionBlock  授权结果回调,需要调用方在appDelegate中调用processAuth_V2Result:standbyCallback:方法获取授权结果
+ *                          若在授权过程中,调用方应用被系统终止则此block无效(此时会调用'processAuth_V2Result:standbyCallback:'传入的standbyCallback)
+ */
+- (void)auth_V2WithInfo:(NSString *)infoStr
+             fromScheme:(NSString *)schemeStr
+               callback:(CompletionBlock)completionBlock;
+
+/**
+ *  处理支付宝app授权后跳回商户app携带的授权结果Url
+ *
+ *  @param resultUrl        支付宝app返回的授权结果url
+ *  @param completionBlock  授权结果回调,用于处理跳转支付宝授权过程中商户APP被系统终止的情况
+ */
+- (void)processAuth_V2Result:(NSURL *)resultUrl
+             standbyCallback:(CompletionBlock)completionBlock;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝授权 1.0 相关接口////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ *  快登授权
+ *  @param authInfo         授权相关信息
+ *  @param completionBlock  授权结果回调,若在授权过程中,调用方应用被系统终止,则此block无效,
+                            需要调用方在appDelegate中调用processAuth_V2Result:standbyCallback:方法获取授权结果
+ */
+- (void)authWithInfo:(APayAuthInfo *)authInfo
+            callback:(CompletionBlock)completionBlock;
+
+/**
+ *  处理支付宝app授权后跳回商户app携带的授权结果Url
+ *
+ *  @param resultUrl        支付宝app返回的授权结果url
+ *  @param completionBlock  授权结果回调
+ */
+- (void)processAuthResult:(NSURL *)resultUrl
+          standbyCallback:(CompletionBlock)completionBlock;
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝 h5 支付转 native 支付接口////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  从h5链接中获取订单串并支付接口(自版本15.4.0起,推荐使用该接口)
+ *
+ *  @param urlStr     拦截的 url string
+ *
+ *  @return YES为成功获取订单信息并发起支付流程;NO为无法获取订单信息,输入url是普通url
+ */
+- (BOOL)payInterceptorWithUrl:(NSString *)urlStr
+                   fromScheme:(NSString *)schemeStr
+                     callback:(CompletionBlock)completionBlock;
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝 tid 相关信息获取接口/////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  获取当前tid相关信息
+ *
+ *  @return tid相关信息
+ */
+- (NSString*)queryTidFactor:(AlipayTidFactor)factor;
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////支付宝支付环境相关信息接口//////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  是否已经使用过
+ *
+ *  @return YES为已经使用过,NO反之
+ */
+- (BOOL)isLogined;
+
+/**
+ *  获取当前版本号
+ *
+ *  @return 当前版本字符串
+ */
+- (NSString *)currentVersion;
+
+/**
+ *  測試所用,realse包无效
+ *
+ *  @param url  测试环境
+ */
+- (void)setUrl:(NSString *)url;
+
+/**
+ *  支付前主动更新本地配置
+ *
+ *  @param block 更新请求结果回调
+ */
+- (void)fetchSdkConfigWithBlock:(void(^)(BOOL success))block;
+
+
+typedef void(^APLogBlock)(NSString *log);
+
+/**
+*   接收AlipaySDK的log信息
+*
+*  @param logBlock 打印log的回调block
+*/
++ (void)startLogWithBlock:(APLogBlock)logBlock;
+
+/**
+*   停止输出log,会释放logBlock
+*
+*
+*/
++ (void)stopLog;
+
+@end

+ 21 - 0
KulexiuForTeacher/Pods/AlipaySDK-iOS/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Antfin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

File diff suppressed because it is too large
+ 35 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/LICENSE.txt


File diff suppressed because it is too large
+ 121 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/README.markdown


File diff suppressed because it is too large
+ 1226 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h


File diff suppressed because it is too large
+ 8526 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m


File diff suppressed because it is too large
+ 1036 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h


File diff suppressed because it is too large
+ 5632 - 0
KulexiuForTeacher/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m


+ 14 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDData.h

@@ -0,0 +1,14 @@
+#import <Foundation/Foundation.h>
+
+@interface NSData (DDData)
+
+- (NSData *)md5Digest;
+
+- (NSData *)sha1Digest;
+
+- (NSString *)hexStringValue;
+
+- (NSString *)base64Encoded;
+- (NSData *)base64Decoded;
+
+@end

+ 158 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDData.m

@@ -0,0 +1,158 @@
+#import "DDData.h"
+#import <CommonCrypto/CommonDigest.h>
+
+
+@implementation NSData (DDData)
+
+static char encodingTable[64] = {
+'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
+
+- (NSData *)md5Digest
+{
+	unsigned char result[CC_MD5_DIGEST_LENGTH];
+    
+    CC_MD5([self bytes], (CC_LONG)[self length], result);
+    return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
+}
+
+- (NSData *)sha1Digest
+{
+	unsigned char result[CC_SHA1_DIGEST_LENGTH];
+    
+	CC_SHA1([self bytes], (CC_LONG)[self length], result);
+    return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH];
+}
+
+- (NSString *)hexStringValue
+{
+	NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)];
+	
+    const unsigned char *dataBuffer = [self bytes];
+    int i;
+    
+    for (i = 0; i < [self length]; ++i)
+	{
+        [stringBuffer appendFormat:@"%02x", (unsigned int)dataBuffer[i]];
+	}
+    
+    return [stringBuffer copy];
+}
+
+- (NSString *)base64Encoded
+{
+	const unsigned char	*bytes = [self bytes];
+	NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
+	unsigned long ixtext = 0;
+	unsigned long lentext = [self length];
+	long ctremaining = 0;
+	unsigned char inbuf[3], outbuf[4];
+	unsigned short i = 0;
+	unsigned short charsonline = 0, ctcopy = 0;
+	unsigned long ix = 0;
+	
+	while( YES )
+	{
+		ctremaining = lentext - ixtext;
+		if( ctremaining <= 0 ) break;
+		
+		for( i = 0; i < 3; i++ ) {
+			ix = ixtext + i;
+			if( ix < lentext ) inbuf[i] = bytes[ix];
+			else inbuf [i] = 0;
+		}
+		
+		outbuf [0] = (inbuf [0] & 0xFC) >> 2;
+		outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
+		outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
+		outbuf [3] = inbuf [2] & 0x3F;
+		ctcopy = 4;
+		
+		switch( ctremaining )
+		{
+			case 1:
+				ctcopy = 2;
+				break;
+			case 2:
+				ctcopy = 3;
+				break;
+		}
+		
+		for( i = 0; i < ctcopy; i++ )
+			[result appendFormat:@"%c", encodingTable[outbuf[i]]];
+		
+		for( i = ctcopy; i < 4; i++ )
+			[result appendString:@"="];
+		
+		ixtext += 3;
+		charsonline += 4;
+	}
+	
+	return [NSString stringWithString:result];
+}
+
+- (NSData *)base64Decoded
+{
+	const unsigned char	*bytes = [self bytes];
+	NSMutableData *result = [NSMutableData dataWithCapacity:[self length]];
+	
+	unsigned long ixtext = 0;
+	unsigned long lentext = [self length];
+	unsigned char ch = 0;
+	unsigned char inbuf[4] = {0, 0, 0, 0};
+	unsigned char outbuf[3] = {0, 0, 0};
+	short i = 0, ixinbuf = 0;
+	BOOL flignore = NO;
+	BOOL flendtext = NO;
+	
+	while( YES )
+	{
+		if( ixtext >= lentext ) break;
+		ch = bytes[ixtext++];
+		flignore = NO;
+		
+		if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
+		else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
+		else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
+		else if( ch == '+' ) ch = 62;
+		else if( ch == '=' ) flendtext = YES;
+		else if( ch == '/' ) ch = 63;
+		else flignore = YES;
+		
+		if( ! flignore )
+		{
+			short ctcharsinbuf = 3;
+			BOOL flbreak = NO;
+			
+			if( flendtext )
+			{
+				if( ! ixinbuf ) break;
+				if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
+				else ctcharsinbuf = 2;
+				ixinbuf = 3;
+				flbreak = YES;
+			}
+			
+			inbuf [ixinbuf++] = ch;
+			
+			if( ixinbuf == 4 )
+			{
+				ixinbuf = 0;
+				outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
+				outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
+				outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
+				
+				for( i = 0; i < ctcharsinbuf; i++ )
+					[result appendBytes:&outbuf[i] length:1];
+			}
+			
+			if( flbreak )  break;
+		}
+	}
+	
+	return [NSData dataWithData:result];
+}
+
+@end

+ 12 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDNumber.h

@@ -0,0 +1,12 @@
+#import <Foundation/Foundation.h>
+
+
+@interface NSNumber (DDNumber)
+
++ (BOOL)parseString:(NSString *)str intoSInt64:(SInt64 *)pNum;
++ (BOOL)parseString:(NSString *)str intoUInt64:(UInt64 *)pNum;
+
++ (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum;
++ (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum;
+
+@end

+ 88 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDNumber.m

@@ -0,0 +1,88 @@
+#import "DDNumber.h"
+
+
+@implementation NSNumber (DDNumber)
+
++ (BOOL)parseString:(NSString *)str intoSInt64:(SInt64 *)pNum
+{
+	if(str == nil)
+	{
+		*pNum = 0;
+		return NO;
+	}
+	
+	errno = 0;
+	
+	// On both 32-bit and 64-bit machines, long long = 64 bit
+	
+	*pNum = strtoll([str UTF8String], NULL, 10);
+	
+	if(errno != 0)
+		return NO;
+	else
+		return YES;
+}
+
++ (BOOL)parseString:(NSString *)str intoUInt64:(UInt64 *)pNum
+{
+	if(str == nil)
+	{
+		*pNum = 0;
+		return NO;
+	}
+	
+	errno = 0;
+	
+	// On both 32-bit and 64-bit machines, unsigned long long = 64 bit
+	
+	*pNum = strtoull([str UTF8String], NULL, 10);
+	
+	if(errno != 0)
+		return NO;
+	else
+		return YES;
+}
+
++ (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum
+{
+	if(str == nil)
+	{
+		*pNum = 0;
+		return NO;
+	}
+	
+	errno = 0;
+	
+	// On LP64, NSInteger = long = 64 bit
+	// Otherwise, NSInteger = int = long = 32 bit
+	
+	*pNum = strtol([str UTF8String], NULL, 10);
+	
+	if(errno != 0)
+		return NO;
+	else
+		return YES;
+}
+
++ (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum
+{
+	if(str == nil)
+	{
+		*pNum = 0;
+		return NO;
+	}
+	
+	errno = 0;
+	
+	// On LP64, NSUInteger = unsigned long = 64 bit
+	// Otherwise, NSUInteger = unsigned int = unsigned long = 32 bit
+	
+	*pNum = strtoul([str UTF8String], NULL, 10);
+	
+	if(errno != 0)
+		return NO;
+	else
+		return YES;
+}
+
+@end

+ 56 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDRange.h

@@ -0,0 +1,56 @@
+/**
+ * DDRange is the functional equivalent of a 64 bit NSRange.
+ * The HTTP Server is designed to support very large files.
+ * On 32 bit architectures (ppc, i386) NSRange uses unsigned 32 bit integers.
+ * This only supports a range of up to 4 gigabytes.
+ * By defining our own variant, we can support a range up to 16 exabytes.
+ * 
+ * All effort is given such that DDRange functions EXACTLY the same as NSRange.
+**/
+
+#import <Foundation/NSValue.h>
+#import <Foundation/NSObjCRuntime.h>
+
+@class NSString;
+
+typedef struct _DDRange {
+    UInt64 location;
+    UInt64 length;
+} DDRange;
+
+typedef DDRange *DDRangePointer;
+
+NS_INLINE DDRange DDMakeRange(UInt64 loc, UInt64 len) {
+    DDRange r;
+    r.location = loc;
+    r.length = len;
+    return r;
+}
+
+NS_INLINE UInt64 DDMaxRange(DDRange range) {
+    return (range.location + range.length);
+}
+
+NS_INLINE BOOL DDLocationInRange(UInt64 loc, DDRange range) {
+    return (loc - range.location < range.length);
+}
+
+NS_INLINE BOOL DDEqualRanges(DDRange range1, DDRange range2) {
+    return ((range1.location == range2.location) && (range1.length == range2.length));
+}
+
+FOUNDATION_EXPORT DDRange DDUnionRange(DDRange range1, DDRange range2);
+FOUNDATION_EXPORT DDRange DDIntersectionRange(DDRange range1, DDRange range2);
+FOUNDATION_EXPORT NSString *DDStringFromRange(DDRange range);
+FOUNDATION_EXPORT DDRange DDRangeFromString(NSString *aString);
+
+NSInteger DDRangeCompare(DDRangePointer pDDRange1, DDRangePointer pDDRange2);
+
+@interface NSValue (NSValueDDRangeExtensions)
+
++ (NSValue *)valueWithDDRange:(DDRange)range;
+- (DDRange)ddrangeValue;
+
+- (NSInteger)ddrangeCompare:(NSValue *)ddrangeValue;
+
+@end

+ 104 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Categories/DDRange.m

@@ -0,0 +1,104 @@
+#import "DDRange.h"
+#import "DDNumber.h"
+
+DDRange DDUnionRange(DDRange range1, DDRange range2)
+{
+	DDRange result;
+	
+	result.location = MIN(range1.location, range2.location);
+	result.length   = MAX(DDMaxRange(range1), DDMaxRange(range2)) - result.location;
+	
+	return result;
+}
+
+DDRange DDIntersectionRange(DDRange range1, DDRange range2)
+{
+	DDRange result;
+	
+	if((DDMaxRange(range1) < range2.location) || (DDMaxRange(range2) < range1.location))
+	{
+		return DDMakeRange(0, 0);
+	}
+	
+	result.location = MAX(range1.location, range2.location);
+	result.length   = MIN(DDMaxRange(range1), DDMaxRange(range2)) - result.location;
+	
+	return result;
+}
+
+NSString *DDStringFromRange(DDRange range)
+{
+	return [NSString stringWithFormat:@"{%qu, %qu}", range.location, range.length];
+}
+
+DDRange DDRangeFromString(NSString *aString)
+{
+	DDRange result = DDMakeRange(0, 0);
+	
+	// NSRange will ignore '-' characters, but not '+' characters
+	NSCharacterSet *cset = [NSCharacterSet characterSetWithCharactersInString:@"+0123456789"];
+	
+	NSScanner *scanner = [NSScanner scannerWithString:aString];
+	[scanner setCharactersToBeSkipped:[cset invertedSet]];
+	
+	NSString *str1 = nil;
+	NSString *str2 = nil;
+	
+	BOOL found1 = [scanner scanCharactersFromSet:cset intoString:&str1];
+	BOOL found2 = [scanner scanCharactersFromSet:cset intoString:&str2];
+	
+	if(found1) [NSNumber parseString:str1 intoUInt64:&result.location];
+	if(found2) [NSNumber parseString:str2 intoUInt64:&result.length];
+	
+	return result;
+}
+
+NSInteger DDRangeCompare(DDRangePointer pDDRange1, DDRangePointer pDDRange2)
+{
+	// Comparison basis:
+	// Which range would you encouter first if you started at zero, and began walking towards infinity.
+	// If you encouter both ranges at the same time, which range would end first.
+	
+	if(pDDRange1->location < pDDRange2->location)
+	{
+		return NSOrderedAscending;
+	}
+	if(pDDRange1->location > pDDRange2->location)
+	{
+		return NSOrderedDescending;
+	}
+	if(pDDRange1->length < pDDRange2->length)
+	{
+		return NSOrderedAscending;
+	}
+	if(pDDRange1->length > pDDRange2->length)
+	{
+		return NSOrderedDescending;
+	}
+	
+	return NSOrderedSame;
+}
+
+@implementation NSValue (NSValueDDRangeExtensions)
+
++ (NSValue *)valueWithDDRange:(DDRange)range
+{
+	return [NSValue valueWithBytes:&range objCType:@encode(DDRange)];
+}
+
+- (DDRange)ddrangeValue
+{
+	DDRange result;
+	[self getValue:&result];
+	return result;
+}
+
+- (NSInteger)ddrangeCompare:(NSValue *)other
+{
+	DDRange r1 = [self ddrangeValue];
+	DDRange r2 = [other ddrangeValue];
+	
+	return DDRangeCompare(&r1, &r2);
+}
+
+@end

+ 45 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h

@@ -0,0 +1,45 @@
+#import <Foundation/Foundation.h>
+
+#if TARGET_OS_IPHONE
+  // Note: You may need to add the CFNetwork Framework to your project
+  #import <CFNetwork/CFNetwork.h>
+#endif
+
+@class HTTPMessage;
+
+
+@interface HTTPAuthenticationRequest : NSObject
+{
+	BOOL isBasic;
+	BOOL isDigest;
+	
+	NSString *base64Credentials;
+	
+	NSString *username;
+	NSString *realm;
+	NSString *nonce;
+	NSString *uri;
+	NSString *qop;
+	NSString *nc;
+	NSString *cnonce;
+	NSString *response;
+}
+- (id)initWithRequest:(HTTPMessage *)request;
+
+- (BOOL)isBasic;
+- (BOOL)isDigest;
+
+// Basic
+- (NSString *)base64Credentials;
+
+// Digest
+- (NSString *)username;
+- (NSString *)realm;
+- (NSString *)nonce;
+- (NSString *)uri;
+- (NSString *)qop;
+- (NSString *)nc;
+- (NSString *)cnonce;
+- (NSString *)response;
+
+@end

+ 195 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m

@@ -0,0 +1,195 @@
+#import "HTTPAuthenticationRequest.h"
+#import "HTTPMessage.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+@interface HTTPAuthenticationRequest (PrivateAPI)
+- (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
+- (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
+@end
+
+
+@implementation HTTPAuthenticationRequest
+
+- (id)initWithRequest:(HTTPMessage *)request
+{
+	if ((self = [super init]))
+	{
+		NSString *authInfo = [request headerField:@"Authorization"];
+		
+		isBasic = NO;
+		if ([authInfo length] >= 6)
+		{
+			isBasic = [[authInfo substringToIndex:6] caseInsensitiveCompare:@"Basic "] == NSOrderedSame;
+		}
+		
+		isDigest = NO;
+		if ([authInfo length] >= 7)
+		{
+			isDigest = [[authInfo substringToIndex:7] caseInsensitiveCompare:@"Digest "] == NSOrderedSame;
+		}
+		
+		if (isBasic)
+		{
+			NSMutableString *temp = [[authInfo substringFromIndex:6] mutableCopy];
+			CFStringTrimWhitespace((__bridge CFMutableStringRef)temp);
+			
+			base64Credentials = [temp copy];
+		}
+		
+		if (isDigest)
+		{
+			username = [self quotedSubHeaderFieldValue:@"username" fromHeaderFieldValue:authInfo];
+			realm    = [self quotedSubHeaderFieldValue:@"realm" fromHeaderFieldValue:authInfo];
+			nonce    = [self quotedSubHeaderFieldValue:@"nonce" fromHeaderFieldValue:authInfo];
+			uri      = [self quotedSubHeaderFieldValue:@"uri" fromHeaderFieldValue:authInfo];
+			
+			// It appears from RFC 2617 that the qop is to be given unquoted
+			// Tests show that Firefox performs this way, but Safari does not
+			// Thus we'll attempt to retrieve the value as nonquoted, but we'll verify it doesn't start with a quote
+			qop      = [self nonquotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
+			if(qop && ([qop characterAtIndex:0] == '"'))
+			{
+				qop  = [self quotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
+			}
+			
+			nc       = [self nonquotedSubHeaderFieldValue:@"nc" fromHeaderFieldValue:authInfo];
+			cnonce   = [self quotedSubHeaderFieldValue:@"cnonce" fromHeaderFieldValue:authInfo];
+			response = [self quotedSubHeaderFieldValue:@"response" fromHeaderFieldValue:authInfo];
+		}
+	}
+	return self;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Accessors:
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)isBasic {
+	return isBasic;
+}
+
+- (BOOL)isDigest {
+	return isDigest;
+}
+
+- (NSString *)base64Credentials {
+	return base64Credentials;
+}
+
+- (NSString *)username {
+	return username;
+}
+
+- (NSString *)realm {
+	return realm;
+}
+
+- (NSString *)nonce {
+	return nonce;
+}
+
+- (NSString *)uri {
+	return uri;
+}
+
+- (NSString *)qop {
+	return qop;
+}
+
+- (NSString *)nc {
+	return nc;
+}
+
+- (NSString *)cnonce {
+	return cnonce;
+}
+
+- (NSString *)response {
+	return response;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Private API:
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Retrieves a "Sub Header Field Value" from a given header field value.
+ * The sub header field is expected to be quoted.
+ * 
+ * In the following header field:
+ * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
+ * The sub header field titled 'username' is quoted, and this method would return the value @"Mufasa".
+**/
+- (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
+{
+	NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=\"", param]];
+	if(startRange.location == NSNotFound)
+	{
+		// The param was not found anywhere in the header
+		return nil;
+	}
+	
+	NSUInteger postStartRangeLocation = startRange.location + startRange.length;
+	NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
+	NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
+	
+	NSRange endRange = [header rangeOfString:@"\"" options:0 range:postStartRange];
+	if(endRange.location == NSNotFound)
+	{
+		// The ending double-quote was not found anywhere in the header
+		return nil;
+	}
+	
+	NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
+	return [header substringWithRange:subHeaderRange];
+}
+
+/**
+ * Retrieves a "Sub Header Field Value" from a given header field value.
+ * The sub header field is expected to not be quoted.
+ * 
+ * In the following header field:
+ * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
+ * The sub header field titled 'qop' is nonquoted, and this method would return the value @"auth".
+**/
+- (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
+{
+	NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=", param]];
+	if(startRange.location == NSNotFound)
+	{
+		// The param was not found anywhere in the header
+		return nil;
+	}
+	
+	NSUInteger postStartRangeLocation = startRange.location + startRange.length;
+	NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
+	NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
+	
+	NSRange endRange = [header rangeOfString:@"," options:0 range:postStartRange];
+	if(endRange.location == NSNotFound)
+	{
+		// The ending comma was not found anywhere in the header
+		// However, if the nonquoted param is at the end of the string, there would be no comma
+		// This is only possible if there are no spaces anywhere
+		NSRange endRange2 = [header rangeOfString:@" " options:0 range:postStartRange];
+		if(endRange2.location != NSNotFound)
+		{
+			return nil;
+		}
+		else
+		{
+			return [header substringWithRange:postStartRange];
+		}
+	}
+	else
+	{
+		NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
+		return [header substringWithRange:subHeaderRange];
+	}
+}
+
+@end

+ 119 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPConnection.h

@@ -0,0 +1,119 @@
+#import <Foundation/Foundation.h>
+
+@class GCDAsyncSocket;
+@class HTTPMessage;
+@class HTTPServer;
+@class WebSocket;
+@protocol HTTPResponse;
+
+
+#define HTTPConnectionDidDieNotification  @"HTTPConnectionDidDie"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface HTTPConfig : NSObject
+{
+	HTTPServer __unsafe_unretained *server;
+	NSString __strong *documentRoot;
+	dispatch_queue_t queue;
+}
+
+- (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot;
+- (id)initWithServer:(HTTPServer *)server documentRoot:(NSString *)documentRoot queue:(dispatch_queue_t)q;
+
+@property (nonatomic, unsafe_unretained, readonly) HTTPServer *server;
+@property (nonatomic, strong, readonly) NSString *documentRoot;
+@property (nonatomic, readonly) dispatch_queue_t queue;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface HTTPConnection : NSObject
+{
+	dispatch_queue_t connectionQueue;
+	GCDAsyncSocket *asyncSocket;
+	HTTPConfig *config;
+	
+	BOOL started;
+	
+	HTTPMessage *request;
+	unsigned int numHeaderLines;
+	
+	BOOL sentResponseHeaders;
+	
+	NSString *nonce;
+	long lastNC;
+	
+	NSObject<HTTPResponse> *httpResponse;
+	
+	NSMutableArray *ranges;
+	NSMutableArray *ranges_headers;
+	NSString *ranges_boundry;
+	int rangeIndex;
+	
+	UInt64 requestContentLength;
+	UInt64 requestContentLengthReceived;
+	UInt64 requestChunkSize;
+	UInt64 requestChunkSizeReceived;
+  
+	NSMutableArray *responseDataSizes;
+}
+
+- (id)initWithAsyncSocket:(GCDAsyncSocket *)newSocket configuration:(HTTPConfig *)aConfig;
+
+- (void)start;
+- (void)stop;
+
+- (void)startConnection;
+
+- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path;
+- (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path;
+
+- (BOOL)isSecureServer;
+- (NSArray *)sslIdentityAndCertificates;
+
+- (BOOL)isPasswordProtected:(NSString *)path;
+- (BOOL)useDigestAccessAuthentication;
+- (NSString *)realm;
+- (NSString *)passwordForUser:(NSString *)username;
+
+- (NSDictionary *)parseParams:(NSString *)query;
+- (NSDictionary *)parseGetParams;
+
+- (NSString *)requestURI;
+
+- (NSArray *)directoryIndexFileNames;
+- (NSString *)filePathForURI:(NSString *)path;
+- (NSString *)filePathForURI:(NSString *)path allowDirectory:(BOOL)allowDirectory;
+- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path;
+- (WebSocket *)webSocketForURI:(NSString *)path;
+
+- (void)prepareForBodyWithSize:(UInt64)contentLength;
+- (void)processBodyData:(NSData *)postDataChunk;
+- (void)finishBody;
+
+- (void)handleVersionNotSupported:(NSString *)version;
+- (void)handleAuthenticationFailed;
+- (void)handleResourceNotFound;
+- (void)handleInvalidRequest:(NSData *)data;
+- (void)handleUnknownMethod:(NSString *)method;
+
+- (NSData *)preprocessResponse:(HTTPMessage *)response;
+- (NSData *)preprocessErrorResponse:(HTTPMessage *)response;
+
+- (void)finishResponse;
+
+- (BOOL)shouldDie;
+- (void)die;
+
+@end
+
+@interface HTTPConnection (AsynchronousHTTPResponse)
+- (void)responseHasAvailableData:(NSObject<HTTPResponse> *)sender;
+- (void)responseDidAbort:(NSObject<HTTPResponse> *)sender;
+@end

File diff suppressed because it is too large
+ 2708 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPConnection.m


+ 136 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPLogging.h

@@ -0,0 +1,136 @@
+/**
+ * In order to provide fast and flexible logging, this project uses Cocoa Lumberjack.
+ * 
+ * The Google Code page has a wealth of documentation if you have any questions.
+ * https://github.com/robbiehanson/CocoaLumberjack
+ * 
+ * Here's what you need to know concerning how logging is setup for CocoaHTTPServer:
+ * 
+ * There are 4 log levels:
+ * - Error
+ * - Warning
+ * - Info
+ * - Verbose
+ * 
+ * In addition to this, there is a Trace flag that can be enabled.
+ * When tracing is enabled, it spits out the methods that are being called.
+ * 
+ * Please note that tracing is separate from the log levels.
+ * For example, one could set the log level to warning, and enable tracing.
+ * 
+ * All logging is asynchronous, except errors.
+ * To use logging within your own custom files, follow the steps below.
+ * 
+ * Step 1:
+ * Import this header in your implementation file:
+ * 
+ * #import "HTTPLogging.h"
+ * 
+ * Step 2:
+ * Define your logging level in your implementation file:
+ * 
+ * // Log levels: off, error, warn, info, verbose
+ * static const int httpLogLevel = HTTP_LOG_LEVEL_VERBOSE;
+ * 
+ * If you wish to enable tracing, you could do something like this:
+ * 
+ * // Debug levels: off, error, warn, info, verbose
+ * static const int httpLogLevel = HTTP_LOG_LEVEL_INFO | HTTP_LOG_FLAG_TRACE;
+ * 
+ * Step 3:
+ * Replace your NSLog statements with HTTPLog statements according to the severity of the message.
+ * 
+ * NSLog(@"Fatal error, no dohickey found!"); -> HTTPLogError(@"Fatal error, no dohickey found!");
+ * 
+ * HTTPLog works exactly the same as NSLog.
+ * This means you can pass it multiple variables just like NSLog.
+**/
+
+#import "DDLog.h"
+
+// Define logging context for every log message coming from the HTTP server.
+// The logging context can be extracted from the DDLogMessage from within the logging framework,
+// which gives loggers, formatters, and filters the ability to optionally process them differently.
+
+#define HTTP_LOG_CONTEXT 80
+
+// Configure log levels.
+
+#define HTTP_LOG_FLAG_ERROR   (1 << 0) // 0...00001
+#define HTTP_LOG_FLAG_WARN    (1 << 1) // 0...00010
+#define HTTP_LOG_FLAG_INFO    (1 << 2) // 0...00100
+#define HTTP_LOG_FLAG_VERBOSE (1 << 3) // 0...01000
+
+#define HTTP_LOG_LEVEL_OFF     0                                              // 0...00000
+#define HTTP_LOG_LEVEL_ERROR   (HTTP_LOG_LEVEL_OFF   | HTTP_LOG_FLAG_ERROR)   // 0...00001
+#define HTTP_LOG_LEVEL_WARN    (HTTP_LOG_LEVEL_ERROR | HTTP_LOG_FLAG_WARN)    // 0...00011
+#define HTTP_LOG_LEVEL_INFO    (HTTP_LOG_LEVEL_WARN  | HTTP_LOG_FLAG_INFO)    // 0...00111
+#define HTTP_LOG_LEVEL_VERBOSE (HTTP_LOG_LEVEL_INFO  | HTTP_LOG_FLAG_VERBOSE) // 0...01111
+
+// Setup fine grained logging.
+// The first 4 bits are being used by the standard log levels (0 - 3)
+// 
+// We're going to add tracing, but NOT as a log level.
+// Tracing can be turned on and off independently of log level.
+
+#define HTTP_LOG_FLAG_TRACE   (1 << 4) // 0...10000
+
+// Setup the usual boolean macros.
+
+#define HTTP_LOG_ERROR   (httpLogLevel & HTTP_LOG_FLAG_ERROR)
+#define HTTP_LOG_WARN    (httpLogLevel & HTTP_LOG_FLAG_WARN)
+#define HTTP_LOG_INFO    (httpLogLevel & HTTP_LOG_FLAG_INFO)
+#define HTTP_LOG_VERBOSE (httpLogLevel & HTTP_LOG_FLAG_VERBOSE)
+#define HTTP_LOG_TRACE   (httpLogLevel & HTTP_LOG_FLAG_TRACE)
+
+// Configure asynchronous logging.
+// We follow the default configuration,
+// but we reserve a special macro to easily disable asynchronous logging for debugging purposes.
+
+#define HTTP_LOG_ASYNC_ENABLED   YES
+
+#define HTTP_LOG_ASYNC_ERROR   ( NO && HTTP_LOG_ASYNC_ENABLED)
+#define HTTP_LOG_ASYNC_WARN    (YES && HTTP_LOG_ASYNC_ENABLED)
+#define HTTP_LOG_ASYNC_INFO    (YES && HTTP_LOG_ASYNC_ENABLED)
+#define HTTP_LOG_ASYNC_VERBOSE (YES && HTTP_LOG_ASYNC_ENABLED)
+#define HTTP_LOG_ASYNC_TRACE   (YES && HTTP_LOG_ASYNC_ENABLED)
+
+// Define logging primitives.
+
+#define HTTPLogError(frmt, ...)    LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_ERROR,   httpLogLevel, HTTP_LOG_FLAG_ERROR,  \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogWarn(frmt, ...)     LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_WARN,    httpLogLevel, HTTP_LOG_FLAG_WARN,   \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogInfo(frmt, ...)     LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_INFO,    httpLogLevel, HTTP_LOG_FLAG_INFO,    \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogVerbose(frmt, ...)  LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_VERBOSE, httpLogLevel, HTTP_LOG_FLAG_VERBOSE, \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogTrace()             LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_TRACE,   httpLogLevel, HTTP_LOG_FLAG_TRACE, \
+                                                  HTTP_LOG_CONTEXT, @"%@[%p]: %@", THIS_FILE, self, THIS_METHOD)
+
+#define HTTPLogTrace2(frmt, ...)   LOG_OBJC_MAYBE(HTTP_LOG_ASYNC_TRACE,   httpLogLevel, HTTP_LOG_FLAG_TRACE, \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+
+#define HTTPLogCError(frmt, ...)      LOG_C_MAYBE(HTTP_LOG_ASYNC_ERROR,   httpLogLevel, HTTP_LOG_FLAG_ERROR,   \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogCWarn(frmt, ...)       LOG_C_MAYBE(HTTP_LOG_ASYNC_WARN,    httpLogLevel, HTTP_LOG_FLAG_WARN,    \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogCInfo(frmt, ...)       LOG_C_MAYBE(HTTP_LOG_ASYNC_INFO,    httpLogLevel, HTTP_LOG_FLAG_INFO,    \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogCVerbose(frmt, ...)    LOG_C_MAYBE(HTTP_LOG_ASYNC_VERBOSE, httpLogLevel, HTTP_LOG_FLAG_VERBOSE, \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+
+#define HTTPLogCTrace()               LOG_C_MAYBE(HTTP_LOG_ASYNC_TRACE,   httpLogLevel, HTTP_LOG_FLAG_TRACE, \
+                                                  HTTP_LOG_CONTEXT, @"%@[%p]: %@", THIS_FILE, self, __FUNCTION__)
+
+#define HTTPLogCTrace2(frmt, ...)     LOG_C_MAYBE(HTTP_LOG_ASYNC_TRACE,   httpLogLevel, HTTP_LOG_FLAG_TRACE, \
+                                                  HTTP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
+

+ 48 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPMessage.h

@@ -0,0 +1,48 @@
+/**
+ * The HTTPMessage class is a simple Objective-C wrapper around Apple's CFHTTPMessage class.
+**/
+
+#import <Foundation/Foundation.h>
+
+#if TARGET_OS_IPHONE
+  // Note: You may need to add the CFNetwork Framework to your project
+  #import <CFNetwork/CFNetwork.h>
+#endif
+
+#define HTTPVersion1_0  ((NSString *)kCFHTTPVersion1_0)
+#define HTTPVersion1_1  ((NSString *)kCFHTTPVersion1_1)
+
+
+@interface HTTPMessage : NSObject
+{
+	CFHTTPMessageRef message;
+}
+
+- (id)initEmptyRequest;
+
+- (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version;
+
+- (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version;
+
+- (BOOL)appendData:(NSData *)data;
+
+- (BOOL)isHeaderComplete;
+
+- (NSString *)version;
+
+- (NSString *)method;
+- (NSURL *)url;
+
+- (NSInteger)statusCode;
+
+- (NSDictionary *)allHeaderFields;
+- (NSString *)headerField:(NSString *)headerField;
+
+- (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue;
+
+- (NSData *)messageData;
+
+- (NSData *)body;
+- (void)setBody:(NSData *)body;
+
+@end

+ 113 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPMessage.m

@@ -0,0 +1,113 @@
+#import "HTTPMessage.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+
+@implementation HTTPMessage
+
+- (id)initEmptyRequest
+{
+	if ((self = [super init]))
+	{
+		message = CFHTTPMessageCreateEmpty(NULL, YES);
+	}
+	return self;
+}
+
+- (id)initRequestWithMethod:(NSString *)method URL:(NSURL *)url version:(NSString *)version
+{
+	if ((self = [super init]))
+	{
+		message = CFHTTPMessageCreateRequest(NULL,
+		                                    (__bridge CFStringRef)method,
+		                                    (__bridge CFURLRef)url,
+		                                    (__bridge CFStringRef)version);
+	}
+	return self;
+}
+
+- (id)initResponseWithStatusCode:(NSInteger)code description:(NSString *)description version:(NSString *)version
+{
+	if ((self = [super init]))
+	{
+		message = CFHTTPMessageCreateResponse(NULL,
+		                                      (CFIndex)code,
+		                                      (__bridge CFStringRef)description,
+		                                      (__bridge CFStringRef)version);
+	}
+	return self;
+}
+
+- (void)dealloc
+{
+	if (message)
+	{
+		CFRelease(message);
+	}
+}
+
+- (BOOL)appendData:(NSData *)data
+{
+	return CFHTTPMessageAppendBytes(message, [data bytes], [data length]);
+}
+
+- (BOOL)isHeaderComplete
+{
+	return CFHTTPMessageIsHeaderComplete(message);
+}
+
+- (NSString *)version
+{
+	return (__bridge_transfer NSString *)CFHTTPMessageCopyVersion(message);
+}
+
+- (NSString *)method
+{
+	return (__bridge_transfer NSString *)CFHTTPMessageCopyRequestMethod(message);
+}
+
+- (NSURL *)url
+{
+	return (__bridge_transfer NSURL *)CFHTTPMessageCopyRequestURL(message);
+}
+
+- (NSInteger)statusCode
+{
+	return (NSInteger)CFHTTPMessageGetResponseStatusCode(message);
+}
+
+- (NSDictionary *)allHeaderFields
+{
+	return (__bridge_transfer NSDictionary *)CFHTTPMessageCopyAllHeaderFields(message);
+}
+
+- (NSString *)headerField:(NSString *)headerField
+{
+	return (__bridge_transfer NSString *)CFHTTPMessageCopyHeaderFieldValue(message, (__bridge CFStringRef)headerField);
+}
+
+- (void)setHeaderField:(NSString *)headerField value:(NSString *)headerFieldValue
+{
+	CFHTTPMessageSetHeaderFieldValue(message,
+	                                 (__bridge CFStringRef)headerField,
+	                                 (__bridge CFStringRef)headerFieldValue);
+}
+
+- (NSData *)messageData
+{
+	return (__bridge_transfer NSData *)CFHTTPMessageCopySerializedMessage(message);
+}
+
+- (NSData *)body
+{
+	return (__bridge_transfer NSData *)CFHTTPMessageCopyBody(message);
+}
+
+- (void)setBody:(NSData *)body
+{
+	CFHTTPMessageSetBody(message, (__bridge CFDataRef)body);
+}
+
+@end

+ 149 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPResponse.h

@@ -0,0 +1,149 @@
+#import <Foundation/Foundation.h>
+
+
+@protocol HTTPResponse
+
+/**
+ * Returns the length of the data in bytes.
+ * If you don't know the length in advance, implement the isChunked method and have it return YES.
+**/
+- (UInt64)contentLength;
+
+/**
+ * The HTTP server supports range requests in order to allow things like
+ * file download resumption and optimized streaming on mobile devices.
+**/
+- (UInt64)offset;
+- (void)setOffset:(UInt64)offset;
+
+/**
+ * Returns the data for the response.
+ * You do not have to return data of the exact length that is given.
+ * You may optionally return data of a lesser length.
+ * However, you must never return data of a greater length than requested.
+ * Doing so could disrupt proper support for range requests.
+ * 
+ * To support asynchronous responses, read the discussion at the bottom of this header.
+**/
+- (NSData *)readDataOfLength:(NSUInteger)length;
+
+/**
+ * Should only return YES after the HTTPConnection has read all available data.
+ * That is, all data for the response has been returned to the HTTPConnection via the readDataOfLength method.
+**/
+- (BOOL)isDone;
+
+@optional
+
+/**
+ * If you need time to calculate any part of the HTTP response headers (status code or header fields),
+ * this method allows you to delay sending the headers so that you may asynchronously execute the calculations.
+ * Simply implement this method and return YES until you have everything you need concerning the headers.
+ * 
+ * This method ties into the asynchronous response architecture of the HTTPConnection.
+ * You should read the full discussion at the bottom of this header.
+ * 
+ * If you return YES from this method,
+ * the HTTPConnection will wait for you to invoke the responseHasAvailableData method.
+ * After you do, the HTTPConnection will again invoke this method to see if the response is ready to send the headers.
+ * 
+ * You should only delay sending the headers until you have everything you need concerning just the headers.
+ * Asynchronously generating the body of the response is not an excuse to delay sending the headers.
+ * Instead you should tie into the asynchronous response architecture, and use techniques such as the isChunked method.
+ * 
+ * Important: You should read the discussion at the bottom of this header.
+**/
+- (BOOL)delayResponseHeaders;
+
+/**
+ * Status code for response.
+ * Allows for responses such as redirect (301), etc.
+**/
+- (NSInteger)status;
+
+/**
+ * If you want to add any extra HTTP headers to the response,
+ * simply return them in a dictionary in this method.
+**/
+- (NSDictionary *)httpHeaders;
+
+/**
+ * If you don't know the content-length in advance,
+ * implement this method in your custom response class and return YES.
+ * 
+ * Important: You should read the discussion at the bottom of this header.
+**/
+- (BOOL)isChunked;
+
+/**
+ * This method is called from the HTTPConnection class when the connection is closed,
+ * or when the connection is finished with the response.
+ * If your response is asynchronous, you should implement this method so you know not to
+ * invoke any methods on the HTTPConnection after this method is called (as the connection may be deallocated).
+**/
+- (void)connectionDidClose;
+
+@end
+
+
+/**
+ * Important notice to those implementing custom asynchronous and/or chunked responses:
+ * 
+ * HTTPConnection supports asynchronous responses.  All you have to do in your custom response class is
+ * asynchronously generate the response, and invoke HTTPConnection's responseHasAvailableData method.
+ * You don't have to wait until you have all of the response ready to invoke this method.  For example, if you
+ * generate the response in incremental chunks, you could call responseHasAvailableData after generating
+ * each chunk.  Please see the HTTPAsyncFileResponse class for an example of how to do this.
+ * 
+ * The normal flow of events for an HTTPConnection while responding to a request is like this:
+ *  - Send http resopnse headers
+ *  - Get data from response via readDataOfLength method.
+ *  - Add data to asyncSocket's write queue.
+ *  - Wait for asyncSocket to notify it that the data has been sent.
+ *  - Get more data from response via readDataOfLength method.
+ *  - ... continue this cycle until the entire response has been sent.
+ * 
+ * With an asynchronous response, the flow is a little different.
+ * 
+ * First the HTTPResponse is given the opportunity to postpone sending the HTTP response headers.
+ * This allows the response to asynchronously execute any code needed to calculate a part of the header.
+ * An example might be the response needs to generate some custom header fields,
+ * or perhaps the response needs to look for a resource on network-attached storage.
+ * Since the network-attached storage may be slow, the response doesn't know whether to send a 200 or 404 yet.
+ * In situations such as this, the HTTPResponse simply implements the delayResponseHeaders method and returns YES.
+ * After returning YES from this method, the HTTPConnection will wait until the response invokes its
+ * responseHasAvailableData method. After this occurs, the HTTPConnection will again query the delayResponseHeaders
+ * method to see if the response is ready to send the headers.
+ * This cycle will continue until the delayResponseHeaders method returns NO.
+ * 
+ * You should only delay sending the response headers until you have everything you need concerning just the headers.
+ * Asynchronously generating the body of the response is not an excuse to delay sending the headers.
+ * 
+ * After the response headers have been sent, the HTTPConnection calls your readDataOfLength method.
+ * You may or may not have any available data at this point. If you don't, then simply return nil.
+ * You should later invoke HTTPConnection's responseHasAvailableData when you have data to send.
+ * 
+ * You don't have to keep track of when you return nil in the readDataOfLength method, or how many times you've invoked
+ * responseHasAvailableData. Just simply call responseHasAvailableData whenever you've generated new data, and
+ * return nil in your readDataOfLength whenever you don't have any available data in the requested range.
+ * HTTPConnection will automatically detect when it should be requesting new data and will act appropriately.
+ * 
+ * It's important that you also keep in mind that the HTTP server supports range requests.
+ * The setOffset method is mandatory, and should not be ignored.
+ * Make sure you take into account the offset within the readDataOfLength method.
+ * You should also be aware that the HTTPConnection automatically sorts any range requests.
+ * So if your setOffset method is called with a value of 100, then you can safely release bytes 0-99.
+ * 
+ * HTTPConnection can also help you keep your memory footprint small.
+ * Imagine you're dynamically generating a 10 MB response.  You probably don't want to load all this data into
+ * RAM, and sit around waiting for HTTPConnection to slowly send it out over the network.  All you need to do
+ * is pay attention to when HTTPConnection requests more data via readDataOfLength.  This is because HTTPConnection
+ * will never allow asyncSocket's write queue to get much bigger than READ_CHUNKSIZE bytes.  You should
+ * consider how you might be able to take advantage of this fact to generate your asynchronous response on demand,
+ * while at the same time keeping your memory footprint small, and your application lightning fast.
+ * 
+ * If you don't know the content-length in advanced, you should also implement the isChunked method.
+ * This means the response will not include a Content-Length header, and will instead use "Transfer-Encoding: chunked".
+ * There's a good chance that if your response is asynchronous and dynamic, it's also chunked.
+ * If your response is chunked, you don't need to worry about range requests.
+**/

+ 205 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPServer.h

@@ -0,0 +1,205 @@
+#import <Foundation/Foundation.h>
+
+@class GCDAsyncSocket;
+@class WebSocket;
+
+#if TARGET_OS_IPHONE
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 // iPhone 4.0
+    #define IMPLEMENTED_PROTOCOLS <NSNetServiceDelegate>
+  #else
+    #define IMPLEMENTED_PROTOCOLS 
+  #endif
+#else
+  #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 // Mac OS X 10.6
+    #define IMPLEMENTED_PROTOCOLS <NSNetServiceDelegate>
+  #else
+    #define IMPLEMENTED_PROTOCOLS 
+  #endif
+#endif
+
+
+@interface HTTPServer : NSObject IMPLEMENTED_PROTOCOLS
+{
+	// Underlying asynchronous TCP/IP socket
+	GCDAsyncSocket *asyncSocket;
+	
+	// Dispatch queues
+	dispatch_queue_t serverQueue;
+	dispatch_queue_t connectionQueue;
+	void *IsOnServerQueueKey;
+	void *IsOnConnectionQueueKey;
+	
+	// HTTP server configuration
+	NSString *documentRoot;
+	Class connectionClass;
+	NSString *interface;
+	UInt16 port;
+	
+	// NSNetService and related variables
+	NSNetService *netService;
+	NSString *domain;
+	NSString *type;
+	NSString *name;
+	NSString *publishedName;
+	NSDictionary *txtRecordDictionary;
+	
+	// Connection management
+	NSMutableArray *connections;
+	NSMutableArray *webSockets;
+	NSLock *connectionsLock;
+	NSLock *webSocketsLock;
+	
+	BOOL isRunning;
+}
+
+/**
+ * Specifies the document root to serve files from.
+ * For example, if you set this to "/Users/<your_username>/Sites",
+ * then it will serve files out of the local Sites directory (including subdirectories).
+ * 
+ * The default value is nil.
+ * The default server configuration will not serve any files until this is set.
+ * 
+ * If you change the documentRoot while the server is running,
+ * the change will affect future incoming http connections.
+**/
+- (NSString *)documentRoot;
+- (void)setDocumentRoot:(NSString *)value;
+
+/**
+ * The connection class is the class used to handle incoming HTTP connections.
+ * 
+ * The default value is [HTTPConnection class].
+ * You can override HTTPConnection, and then set this to [MyHTTPConnection class].
+ * 
+ * If you change the connectionClass while the server is running,
+ * the change will affect future incoming http connections.
+**/
+- (Class)connectionClass;
+- (void)setConnectionClass:(Class)value;
+
+/**
+ * Set what interface you'd like the server to listen on.
+ * By default this is nil, which causes the server to listen on all available interfaces like en1, wifi etc.
+ * 
+ * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34").
+ * You may also use the special strings "localhost" or "loopback" to specify that
+ * the socket only accept connections from the local machine.
+**/
+- (NSString *)interface;
+- (void)setInterface:(NSString *)value;
+
+/**
+ * The port number to run the HTTP server on.
+ * 
+ * The default port number is zero, meaning the server will automatically use any available port.
+ * This is the recommended port value, as it avoids possible port conflicts with other applications.
+ * Technologies such as Bonjour can be used to allow other applications to automatically discover the port number.
+ * 
+ * Note: As is common on most OS's, you need root privledges to bind to port numbers below 1024.
+ * 
+ * You can change the port property while the server is running, but it won't affect the running server.
+ * To actually change the port the server is listening for connections on you'll need to restart the server.
+ * 
+ * The listeningPort method will always return the port number the running server is listening for connections on.
+ * If the server is not running this method returns 0.
+**/
+- (UInt16)port;
+- (UInt16)listeningPort;
+- (void)setPort:(UInt16)value;
+
+/**
+ * Bonjour domain for publishing the service.
+ * The default value is "local.".
+ * 
+ * Note: Bonjour publishing requires you set a type.
+ * 
+ * If you change the domain property after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+**/
+- (NSString *)domain;
+- (void)setDomain:(NSString *)value;
+
+/**
+ * Bonjour name for publishing the service.
+ * The default value is "".
+ * 
+ * If using an empty string ("") for the service name when registering,
+ * the system will automatically use the "Computer Name".
+ * Using an empty string will also handle name conflicts
+ * by automatically appending a digit to the end of the name.
+ * 
+ * Note: Bonjour publishing requires you set a type.
+ * 
+ * If you change the name after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+ * 
+ * The publishedName method will always return the actual name that was published via the bonjour service.
+ * If the service is not running this method returns nil.
+**/
+- (NSString *)name;
+- (NSString *)publishedName;
+- (void)setName:(NSString *)value;
+
+/**
+ * Bonjour type for publishing the service.
+ * The default value is nil.
+ * The service will not be published via bonjour unless the type is set.
+ * 
+ * If you wish to publish the service as a traditional HTTP server, you should set the type to be "_http._tcp.".
+ * 
+ * If you change the type after the bonjour service has already been published (server already started),
+ * you'll need to invoke the republishBonjour method to update the broadcasted bonjour service.
+**/
+- (NSString *)type;
+- (void)setType:(NSString *)value;
+
+/**
+ * Republishes the service via bonjour if the server is running.
+ * If the service was not previously published, this method will publish it (if the server is running).
+**/
+- (void)republishBonjour;
+
+/**
+ * 
+**/
+- (NSDictionary *)TXTRecordDictionary;
+- (void)setTXTRecordDictionary:(NSDictionary *)dict;
+
+/**
+ * Attempts to starts the server on the configured port, interface, etc.
+ * 
+ * If an error occurs, this method returns NO and sets the errPtr (if given).
+ * Otherwise returns YES on success.
+ * 
+ * Some examples of errors that might occur:
+ * - You specified the server listen on a port which is already in use by another application.
+ * - You specified the server listen on a port number below 1024, which requires root priviledges.
+ * 
+ * Code Example:
+ * 
+ * NSError *err = nil;
+ * if (![httpServer start:&err])
+ * {
+ *     NSLog(@"Error starting http server: %@", err);
+ * }
+**/
+- (BOOL)start:(NSError **)errPtr;
+
+/**
+ * Stops the server, preventing it from accepting any new connections.
+ * You may specify whether or not you want to close the existing client connections.
+ * 
+ * The default stop method (with no arguments) will close any existing connections. (It invokes [self stop:NO])
+**/
+- (void)stop;
+- (void)stop:(BOOL)keepExistingConnections;
+
+- (BOOL)isRunning;
+
+- (void)addWebSocket:(WebSocket *)ws;
+
+- (NSUInteger)numberOfHTTPConnections;
+- (NSUInteger)numberOfWebSocketConnections;
+
+@end

+ 772 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/HTTPServer.m

@@ -0,0 +1,772 @@
+#import "HTTPServer.h"
+#import "GCDAsyncSocket.h"
+#import "HTTPConnection.h"
+#import "WebSocket.h"
+#import "HTTPLogging.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels: off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_INFO; // | HTTP_LOG_FLAG_TRACE;
+
+@interface HTTPServer (PrivateAPI)
+
+- (void)unpublishBonjour;
+- (void)publishBonjour;
+
++ (void)startBonjourThreadIfNeeded;
++ (void)performBonjourBlock:(dispatch_block_t)block;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation HTTPServer
+
+/**
+ * Standard Constructor.
+ * Instantiates an HTTP server, but does not start it.
+**/
+- (id)init
+{
+	if ((self = [super init]))
+	{
+		HTTPLogTrace();
+		
+		// Setup underlying dispatch queues
+		serverQueue = dispatch_queue_create("HTTPServer", NULL);
+		connectionQueue = dispatch_queue_create("HTTPConnection", NULL);
+		
+		IsOnServerQueueKey = &IsOnServerQueueKey;
+		IsOnConnectionQueueKey = &IsOnConnectionQueueKey;
+		
+		void *nonNullUnusedPointer = (__bridge void *)self; // Whatever, just not null
+		
+		dispatch_queue_set_specific(serverQueue, IsOnServerQueueKey, nonNullUnusedPointer, NULL);
+		dispatch_queue_set_specific(connectionQueue, IsOnConnectionQueueKey, nonNullUnusedPointer, NULL);
+		
+		// Initialize underlying GCD based tcp socket
+		asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:serverQueue];
+		
+		// Use default connection class of HTTPConnection
+		connectionClass = [HTTPConnection self];
+		
+		// By default bind on all available interfaces, en1, wifi etc
+		interface = nil;
+		
+		// Use a default port of 0
+		// This will allow the kernel to automatically pick an open port for us
+		port = 0;
+		
+		// Configure default values for bonjour service
+		
+		// Bonjour domain. Use the local domain by default
+		domain = @"local.";
+		
+		// If using an empty string ("") for the service name when registering,
+		// the system will automatically use the "Computer Name".
+		// Passing in an empty string will also handle name conflicts
+		// by automatically appending a digit to the end of the name.
+		name = @"";
+		
+		// Initialize arrays to hold all the HTTP and webSocket connections
+		connections = [[NSMutableArray alloc] init];
+		webSockets  = [[NSMutableArray alloc] init];
+		
+		connectionsLock = [[NSLock alloc] init];
+		webSocketsLock  = [[NSLock alloc] init];
+		
+		// Register for notifications of closed connections
+		[[NSNotificationCenter defaultCenter] addObserver:self
+		                                         selector:@selector(connectionDidDie:)
+		                                             name:HTTPConnectionDidDieNotification
+		                                           object:nil];
+		
+		// Register for notifications of closed websocket connections
+		[[NSNotificationCenter defaultCenter] addObserver:self
+		                                         selector:@selector(webSocketDidDie:)
+		                                             name:WebSocketDidDieNotification
+		                                           object:nil];
+		
+		isRunning = NO;
+	}
+	return self;
+}
+
+/**
+ * Standard Deconstructor.
+ * Stops the server, and clients, and releases any resources connected with this instance.
+**/
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+	// Remove notification observer
+	[[NSNotificationCenter defaultCenter] removeObserver:self];
+	
+	// Stop the server if it's running
+	[self stop];
+	
+	// Release all instance variables
+	
+	#if !OS_OBJECT_USE_OBJC
+	dispatch_release(serverQueue);
+	dispatch_release(connectionQueue);
+	#endif
+	
+	[asyncSocket setDelegate:nil delegateQueue:NULL];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Server Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The document root is filesystem root for the webserver.
+ * Thus requests for /index.html will be referencing the index.html file within the document root directory.
+ * All file requests are relative to this document root.
+**/
+- (NSString *)documentRoot
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = documentRoot;
+	});
+	
+	return result;
+}
+
+- (void)setDocumentRoot:(NSString *)value
+{
+	HTTPLogTrace();
+	
+	// Document root used to be of type NSURL.
+	// Add type checking for early warning to developers upgrading from older versions.
+	
+	if (value && ![value isKindOfClass:[NSString class]])
+	{
+		HTTPLogWarn(@"%@: %@ - Expecting NSString parameter, received %@ parameter",
+					THIS_FILE, THIS_METHOD, NSStringFromClass([value class]));
+		return;
+	}
+	
+	NSString *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+		documentRoot = valueCopy;
+	});
+	
+}
+
+/**
+ * The connection class is the class that will be used to handle connections.
+ * That is, when a new connection is created, an instance of this class will be intialized.
+ * The default connection class is HTTPConnection.
+ * If you use a different connection class, it is assumed that the class extends HTTPConnection
+**/
+- (Class)connectionClass
+{
+	__block Class result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = connectionClass;
+	});
+	
+	return result;
+}
+
+- (void)setConnectionClass:(Class)value
+{
+	HTTPLogTrace();
+	
+	dispatch_async(serverQueue, ^{
+		connectionClass = value;
+	});
+}
+
+/**
+ * What interface to bind the listening socket to.
+**/
+- (NSString *)interface
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = interface;
+	});
+	
+	return result;
+}
+
+- (void)setInterface:(NSString *)value
+{
+	NSString *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+		interface = valueCopy;
+	});
+	
+}
+
+/**
+ * The port to listen for connections on.
+ * By default this port is initially set to zero, which allows the kernel to pick an available port for us.
+ * After the HTTP server has started, the port being used may be obtained by this method.
+**/
+- (UInt16)port
+{
+	__block UInt16 result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = port;
+	});
+	
+    return result;
+}
+
+- (UInt16)listeningPort
+{
+	__block UInt16 result;
+	
+	dispatch_sync(serverQueue, ^{
+		if (isRunning)
+			result = [asyncSocket localPort];
+		else
+			result = 0;
+	});
+	
+	return result;
+}
+
+- (void)setPort:(UInt16)value
+{
+	HTTPLogTrace();
+	
+	dispatch_async(serverQueue, ^{
+		port = value;
+	});
+}
+
+/**
+ * Domain on which to broadcast this service via Bonjour.
+ * The default domain is @"local".
+**/
+- (NSString *)domain
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = domain;
+	});
+	
+    return result;
+}
+
+- (void)setDomain:(NSString *)value
+{
+	HTTPLogTrace();
+	
+	NSString *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+		domain = valueCopy;
+	});
+	
+}
+
+/**
+ * The name to use for this service via Bonjour.
+ * The default name is an empty string,
+ * which should result in the published name being the host name of the computer.
+**/
+- (NSString *)name
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = name;
+	});
+	
+	return result;
+}
+
+- (NSString *)publishedName
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		
+		if (netService == nil)
+		{
+			result = nil;
+		}
+		else
+		{
+			
+			dispatch_block_t bonjourBlock = ^{
+				result = [[netService name] copy];
+			};
+			
+			[[self class] performBonjourBlock:bonjourBlock];
+		}
+	});
+	
+	return result;
+}
+
+- (void)setName:(NSString *)value
+{
+	NSString *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+		name = valueCopy;
+	});
+	
+}
+
+/**
+ * The type of service to publish via Bonjour.
+ * No type is set by default, and one must be set in order for the service to be published.
+**/
+- (NSString *)type
+{
+	__block NSString *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = type;
+	});
+	
+	return result;
+}
+
+- (void)setType:(NSString *)value
+{
+	NSString *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+		type = valueCopy;
+	});
+	
+}
+
+/**
+ * The extra data to use for this service via Bonjour.
+**/
+- (NSDictionary *)TXTRecordDictionary
+{
+	__block NSDictionary *result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = txtRecordDictionary;
+	});
+	
+	return result;
+}
+
+- (void)setTXTRecordDictionary:(NSDictionary *)value
+{
+	HTTPLogTrace();
+	
+	NSDictionary *valueCopy = [value copy];
+	
+	dispatch_async(serverQueue, ^{
+	
+		txtRecordDictionary = valueCopy;
+		
+		// Update the txtRecord of the netService if it has already been published
+		if (netService)
+		{
+			NSNetService *theNetService = netService;
+			NSData *txtRecordData = nil;
+			if (txtRecordDictionary)
+				txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
+			
+			dispatch_block_t bonjourBlock = ^{
+				[theNetService setTXTRecordData:txtRecordData];
+			};
+			
+			[[self class] performBonjourBlock:bonjourBlock];
+		}
+	});
+	
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Server Control
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)start:(NSError **)errPtr
+{
+	HTTPLogTrace();
+	
+	__block BOOL success = YES;
+	__block NSError *err = nil;
+	
+	dispatch_sync(serverQueue, ^{ @autoreleasepool {
+		
+		success = [asyncSocket acceptOnInterface:interface port:port error:&err];
+		if (success)
+		{
+			HTTPLogInfo(@"%@: Started HTTP server on port %hu", THIS_FILE, [asyncSocket localPort]);
+			
+			isRunning = YES;
+			[self publishBonjour];
+		}
+		else
+		{
+			HTTPLogError(@"%@: Failed to start HTTP Server: %@", THIS_FILE, err);
+		}
+	}});
+	
+	if (errPtr)
+		*errPtr = err;
+	
+	return success;
+}
+
+- (void)stop
+{
+	[self stop:NO];
+}
+
+- (void)stop:(BOOL)keepExistingConnections
+{
+	HTTPLogTrace();
+	
+	dispatch_sync(serverQueue, ^{ @autoreleasepool {
+		
+		// First stop publishing the service via bonjour
+		[self unpublishBonjour];
+		
+		// Stop listening / accepting incoming connections
+		[asyncSocket disconnect];
+		isRunning = NO;
+		
+		if (!keepExistingConnections)
+		{
+			// Stop all HTTP connections the server owns
+			[connectionsLock lock];
+			for (HTTPConnection *connection in connections)
+			{
+				[connection stop];
+			}
+			[connections removeAllObjects];
+			[connectionsLock unlock];
+			
+			// Stop all WebSocket connections the server owns
+			[webSocketsLock lock];
+			for (WebSocket *webSocket in webSockets)
+			{
+				[webSocket stop];
+			}
+			[webSockets removeAllObjects];
+			[webSocketsLock unlock];
+		}
+	}});
+}
+
+- (BOOL)isRunning
+{
+	__block BOOL result;
+	
+	dispatch_sync(serverQueue, ^{
+		result = isRunning;
+	});
+	
+	return result;
+}
+
+- (void)addWebSocket:(WebSocket *)ws
+{
+	[webSocketsLock lock];
+	
+	HTTPLogTrace();
+	[webSockets addObject:ws];
+	
+	[webSocketsLock unlock];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Server Status
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns the number of http client connections that are currently connected to the server.
+**/
+- (NSUInteger)numberOfHTTPConnections
+{
+	NSUInteger result = 0;
+	
+	[connectionsLock lock];
+	result = [connections count];
+	[connectionsLock unlock];
+	
+	return result;
+}
+
+/**
+ * Returns the number of websocket client connections that are currently connected to the server.
+**/
+- (NSUInteger)numberOfWebSocketConnections
+{
+	NSUInteger result = 0;
+	
+	[webSocketsLock lock];
+	result = [webSockets count];
+	[webSocketsLock unlock];
+	
+	return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Incoming Connections
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (HTTPConfig *)config
+{
+	// Override me if you want to provide a custom config to the new connection.
+	// 
+	// Generally this involves overriding the HTTPConfig class to include any custom settings,
+	// and then having this method return an instance of 'MyHTTPConfig'.
+	
+	// Note: Think you can make the server faster by putting each connection on its own queue?
+	// Then benchmark it before and after and discover for yourself the shocking truth!
+	// 
+	// Try the apache benchmark tool (already installed on your Mac):
+	// $  ab -n 1000 -c 1 http://localhost:<port>/some_path.html
+	
+	return [[HTTPConfig alloc] initWithServer:self documentRoot:documentRoot queue:connectionQueue];
+}
+
+- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
+{
+	HTTPConnection *newConnection = (HTTPConnection *)[[connectionClass alloc] initWithAsyncSocket:newSocket
+	                                                                                 configuration:[self config]];
+	[connectionsLock lock];
+	[connections addObject:newConnection];
+	[connectionsLock unlock];
+	
+	[newConnection start];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Bonjour
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)publishBonjour
+{
+	HTTPLogTrace();
+	
+	NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
+	
+	if (type)
+	{
+		netService = [[NSNetService alloc] initWithDomain:domain type:type name:name port:[asyncSocket localPort]];
+		[netService setDelegate:self];
+		
+		NSNetService *theNetService = netService;
+		NSData *txtRecordData = nil;
+		if (txtRecordDictionary)
+			txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
+		
+		dispatch_block_t bonjourBlock = ^{
+			
+			[theNetService removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+			[theNetService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+			[theNetService publish];
+			
+			// Do not set the txtRecordDictionary prior to publishing!!!
+			// This will cause the OS to crash!!!
+			if (txtRecordData)
+			{
+				[theNetService setTXTRecordData:txtRecordData];
+			}
+		};
+		
+		[[self class] startBonjourThreadIfNeeded];
+		[[self class] performBonjourBlock:bonjourBlock];
+	}
+}
+
+- (void)unpublishBonjour
+{
+	HTTPLogTrace();
+	
+	NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
+	
+	if (netService)
+	{
+		NSNetService *theNetService = netService;
+		
+		dispatch_block_t bonjourBlock = ^{
+			
+			[theNetService stop];
+		};
+		
+		[[self class] performBonjourBlock:bonjourBlock];
+		
+		netService = nil;
+	}
+}
+
+/**
+ * Republishes the service via bonjour if the server is running.
+ * If the service was not previously published, this method will publish it (if the server is running).
+**/
+- (void)republishBonjour
+{
+	HTTPLogTrace();
+	
+	dispatch_async(serverQueue, ^{
+		
+		[self unpublishBonjour];
+		[self publishBonjour];
+	});
+}
+
+/**
+ * Called when our bonjour service has been successfully published.
+ * This method does nothing but output a log message telling us about the published service.
+**/
+- (void)netServiceDidPublish:(NSNetService *)ns
+{
+	// Override me to do something here...
+	// 
+	// Note: This method is invoked on our bonjour thread.
+	
+	HTTPLogInfo(@"Bonjour Service Published: domain(%@) type(%@) name(%@)", [ns domain], [ns type], [ns name]);
+}
+
+/**
+ * Called if our bonjour service failed to publish itself.
+ * This method does nothing but output a log message telling us about the published service.
+**/
+- (void)netService:(NSNetService *)ns didNotPublish:(NSDictionary *)errorDict
+{
+	// Override me to do something here...
+	// 
+	// Note: This method in invoked on our bonjour thread.
+	
+	HTTPLogWarn(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@",
+	                                         [ns domain], [ns type], [ns name], errorDict);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Notifications
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This method is automatically called when a notification of type HTTPConnectionDidDieNotification is posted.
+ * It allows us to remove the connection from our array.
+**/
+- (void)connectionDidDie:(NSNotification *)notification
+{
+	// Note: This method is called on the connection queue that posted the notification
+	
+	[connectionsLock lock];
+	
+	HTTPLogTrace();
+	[connections removeObject:[notification object]];
+	
+	[connectionsLock unlock];
+}
+
+/**
+ * This method is automatically called when a notification of type WebSocketDidDieNotification is posted.
+ * It allows us to remove the websocket from our array.
+**/
+- (void)webSocketDidDie:(NSNotification *)notification
+{
+	// Note: This method is called on the connection queue that posted the notification
+	
+	[webSocketsLock lock];
+	
+	HTTPLogTrace();
+	[webSockets removeObject:[notification object]];
+	
+	[webSocketsLock unlock];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Bonjour Thread
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * NSNetService is runloop based, so it requires a thread with a runloop.
+ * This gives us two options:
+ * 
+ * - Use the main thread
+ * - Setup our own dedicated thread
+ * 
+ * Since we have various blocks of code that need to synchronously access the netservice objects,
+ * using the main thread becomes troublesome and a potential for deadlock.
+**/
+
+static NSThread *bonjourThread;
+
++ (void)startBonjourThreadIfNeeded
+{
+	HTTPLogTrace();
+	
+	static dispatch_once_t predicate;
+	dispatch_once(&predicate, ^{
+		
+		HTTPLogVerbose(@"%@: Starting bonjour thread...", THIS_FILE);
+		
+		bonjourThread = [[NSThread alloc] initWithTarget:self
+		                                        selector:@selector(bonjourThread)
+		                                          object:nil];
+		[bonjourThread start];
+	});
+}
+
++ (void)bonjourThread
+{
+	@autoreleasepool {
+	
+		HTTPLogVerbose(@"%@: BonjourThread: Started", THIS_FILE);
+		
+		// We can't run the run loop unless it has an associated input source or a timer.
+		// So we'll just create a timer that will never fire - unless the server runs for 10,000 years.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+		[NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
+		                                 target:self
+		                               selector:@selector(donothingatall:)
+		                               userInfo:nil
+		                                repeats:YES];
+#pragma clang diagnostic pop
+
+		[[NSRunLoop currentRunLoop] run];
+		
+		HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);
+	
+	}
+}
+
++ (void)executeBonjourBlock:(dispatch_block_t)block
+{
+	HTTPLogTrace();
+	
+	NSAssert([NSThread currentThread] == bonjourThread, @"Executed on incorrect thread");
+	
+	block();
+}
+
++ (void)performBonjourBlock:(dispatch_block_t)block
+{
+	HTTPLogTrace();
+	
+	[self performSelector:@selector(executeBonjourBlock:)
+	             onThread:bonjourThread
+	           withObject:block
+	        waitUntilDone:YES];
+}
+
+@end

+ 65 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h

@@ -0,0 +1,65 @@
+
+#import "MultipartMessageHeader.h"
+
+/* 
+Part one: http://tools.ietf.org/html/rfc2045 (Format of Internet Message Bodies)
+Part two: http://tools.ietf.org/html/rfc2046 (Media Types)
+Part three: http://tools.ietf.org/html/rfc2047 (Message Header Extensions for Non-ASCII Text)
+Part four: http://tools.ietf.org/html/rfc4289 (Registration Procedures) 
+Part five: http://tools.ietf.org/html/rfc2049 (Conformance Criteria and Examples) 
+ 
+Internet message format:  http://tools.ietf.org/html/rfc2822
+
+Multipart/form-data http://tools.ietf.org/html/rfc2388
+*/
+
+@class MultipartFormDataParser;
+
+//-----------------------------------------------------------------
+// protocol MultipartFormDataParser
+//-----------------------------------------------------------------
+
+@protocol MultipartFormDataParserDelegate <NSObject> 
+@optional
+- (void) processContent:(NSData*) data WithHeader:(MultipartMessageHeader*) header;
+- (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header;
+- (void) processPreambleData:(NSData*) data;
+- (void) processEpilogueData:(NSData*) data;
+- (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header;
+@end
+
+//-----------------------------------------------------------------
+// interface MultipartFormDataParser
+//-----------------------------------------------------------------
+
+@interface MultipartFormDataParser : NSObject {
+NSMutableData*						pendingData;
+    NSData*							boundaryData;
+    MultipartMessageHeader*			currentHeader;
+
+	BOOL							waitingForCRLF;
+	BOOL							reachedEpilogue;
+	BOOL							processedPreamble;
+	BOOL							checkForContentEnd;
+
+#if __has_feature(objc_arc_weak)
+	__weak id<MultipartFormDataParserDelegate>                  delegate;
+#else
+	__unsafe_unretained id<MultipartFormDataParserDelegate>     delegate;
+#endif	
+	int									currentEncoding;
+	NSStringEncoding					formEncoding;
+}
+
+- (BOOL) appendData:(NSData*) data;
+
+- (id) initWithBoundary:(NSString*) boundary formEncoding:(NSStringEncoding) formEncoding;
+
+#if __has_feature(objc_arc_weak)
+    @property(weak, readwrite) id delegate;
+#else
+    @property(unsafe_unretained, readwrite) id delegate;
+#endif
+@property(readwrite) NSStringEncoding	formEncoding;
+
+@end

+ 529 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.m

@@ -0,0 +1,529 @@
+
+#import "MultipartFormDataParser.h"
+#import "DDData.h"
+#import "HTTPLogging.h"
+
+#pragma mark log level
+
+#ifdef DEBUG
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#else
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#endif
+
+#ifdef __x86_64__
+#define FMTNSINT "li"
+#else
+#define FMTNSINT "i"
+#endif
+
+
+//-----------------------------------------------------------------
+// interface MultipartFormDataParser (private)
+//-----------------------------------------------------------------
+
+
+@interface MultipartFormDataParser (private)
++ (NSData*) decodedDataFromData:(NSData*) data encoding:(int) encoding;
+
+- (int) findHeaderEnd:(NSData*) workingData fromOffset:(int) offset;
+- (int) findContentEnd:(NSData*) data fromOffset:(int) offset;
+
+- (int) numberOfBytesToLeavePendingWithData:(NSData*) data length:(NSUInteger) length encoding:(int) encoding;
+- (int) offsetTillNewlineSinceOffset:(int) offset inData:(NSData*) data;
+
+- (int) processPreamble:(NSData*) workingData;
+
+@end
+
+
+//-----------------------------------------------------------------
+// implementation MultipartFormDataParser
+//-----------------------------------------------------------------
+
+
+@implementation MultipartFormDataParser 
+@synthesize delegate,formEncoding;
+
+- (id) initWithBoundary:(NSString*) boundary formEncoding:(NSStringEncoding) _formEncoding {
+    if( nil == (self = [super init]) ){
+        return self;
+    }
+	if( nil == boundary ) {
+		HTTPLogWarn(@"MultipartFormDataParser: init with zero boundary");
+		return nil;
+	}
+    boundaryData = [[@"\r\n--" stringByAppendingString:boundary] dataUsingEncoding:NSASCIIStringEncoding];
+
+    pendingData = [[NSMutableData alloc] init];
+    currentEncoding = contentTransferEncoding_binary;
+	currentHeader = nil;
+
+	formEncoding = _formEncoding;
+	reachedEpilogue = NO;
+	processedPreamble = NO;
+
+    return self;
+}
+
+
+- (BOOL) appendData:(NSData *)data { 
+    // Can't parse without boundary;
+    if( nil == boundaryData ) {
+		HTTPLogError(@"MultipartFormDataParser: Trying to parse multipart without specifying a valid boundary");
+		assert(false);
+        return NO;
+    }
+    NSData* workingData = data;
+
+    if( pendingData.length ) {
+        [pendingData appendData:data];
+        workingData = pendingData;
+    }
+
+	// the parser saves parse stat in the offset variable, which indicates offset of unhandled part in 
+	// currently received chunk. Before returning, we always drop all data up to offset, leaving 
+	// only unhandled for the next call
+
+    int offset = 0;
+
+	// don't parse data unless its size is greater then boundary length, so we couldn't
+	// misfind the boundary, if it got split into different data chunks
+	NSUInteger sizeToLeavePending = boundaryData.length;
+
+	if( !reachedEpilogue && workingData.length <= sizeToLeavePending )  {
+		// not enough data even to start parsing.
+		// save to pending data.
+		if( !pendingData.length ) {
+			[pendingData appendData:data];
+		}
+		if( checkForContentEnd ) {
+			if(	pendingData.length >= 2 ) {
+				if( *(uint16_t*)(pendingData.bytes + offset) == 0x2D2D ) {
+					// we found the multipart end. all coming next is an epilogue.
+					HTTPLogVerbose(@"MultipartFormDataParser: End of multipart message");
+					waitingForCRLF = YES;
+					reachedEpilogue = YES;
+					offset+= 2;
+				}
+				else {
+					checkForContentEnd = NO;
+					waitingForCRLF = YES;
+					return YES;
+				}
+			} else {
+				return YES;
+			}
+			
+		}
+		else {
+			return YES;
+		}
+	}
+	while( true ) {
+		if( checkForContentEnd ) {
+			// the flag will be raised to check if the last part was the last one.
+			if( offset < workingData.length -1 ) {
+				char* bytes = (char*) workingData.bytes;
+				if( *(uint16_t*)(bytes + offset) == 0x2D2D ) {
+					// we found the multipart end. all coming next is an epilogue.
+					HTTPLogVerbose(@"MultipartFormDataParser: End of multipart message");
+					checkForContentEnd = NO;
+					reachedEpilogue = YES;
+					// still wait for CRLF, that comes after boundary, but before epilogue.
+					waitingForCRLF = YES;
+					offset += 2;
+				}
+				else {
+					// it's not content end, we have to wait till separator line end before next part comes
+					waitingForCRLF = YES;
+					checkForContentEnd = NO;
+				}
+			}
+			else {
+				// we haven't got enough data to check for content end.
+				// save current unhandled data (it may be 1 byte) to pending and recheck on next chunk received
+				if( offset < workingData.length ) {
+					[pendingData setData:[NSData dataWithBytes:workingData.bytes + workingData.length-1 length:1]];
+				}
+				else {
+					// there is no unhandled data now, wait for more chunks
+					[pendingData setData:[NSData data]];
+				}
+				return YES;
+			}
+		}
+		if( waitingForCRLF ) {
+
+			// the flag will be raised in the code below, meaning, we've read the boundary, but
+			// didnt find the end of boundary line yet.
+
+			offset = [self offsetTillNewlineSinceOffset:offset inData:workingData];
+			if( -1 == offset ) {
+				// didnt find the endl again.
+				if( offset ) {
+					// we still have to save the unhandled data (maybe it's 1 byte CR)
+					if( *((char*)workingData.bytes + workingData.length -1) == '\r' ) {
+						[pendingData setData:[NSData dataWithBytes:workingData.bytes + workingData.length-1 length:1]];
+					}
+					else {
+						// or save nothing if it wasnt 
+						[pendingData setData:[NSData data]];
+					}
+				}
+				return YES;
+			}
+			waitingForCRLF = NO;
+		}
+		if( !processedPreamble ) {
+			// got to find the first boundary before the actual content begins.
+			offset = [self processPreamble:workingData];
+			// wait for more data for preamble
+			if( -1 == offset ) 
+				return YES;
+			// invoke continue to skip newline after boundary.
+			continue;
+		}
+
+		if( reachedEpilogue ) {
+			// parse all epilogue data to delegate.
+			if( [delegate respondsToSelector:@selector(processEpilogueData:)] ) {
+				NSData* epilogueData = [NSData dataWithBytesNoCopy: (char*) workingData.bytes + offset length: workingData.length - offset freeWhenDone:NO];
+				[delegate processEpilogueData: epilogueData];
+			}
+			return YES;
+		}
+
+		if( nil == currentHeader ) {
+			// nil == currentHeader is a state flag, indicating we are waiting for header now.
+			// whenever part is over, currentHeader is set to nil.
+
+			// try to find CRLFCRLF bytes in the data, which indicates header end.
+			// we won't parse header parts, as they won't be too large.
+			int headerEnd = [self findHeaderEnd:workingData fromOffset:offset];
+			if( -1 == headerEnd ) {
+				// didn't recieve the full header yet.
+				if( !pendingData.length) {
+					// store the unprocessed data till next chunks come
+					[pendingData appendBytes:data.bytes + offset length:data.length - offset];
+				}
+				else {
+					if( offset ) {
+						// save the current parse state; drop all handled data and save unhandled only.
+						pendingData = [[NSMutableData alloc] initWithBytes: (char*) workingData.bytes + offset length:workingData.length - offset];
+					}
+				}
+				return  YES;
+			}
+			else {
+
+				// let the header parser do it's job from now on.
+				NSData * headerData = [NSData dataWithBytesNoCopy: (char*) workingData.bytes + offset length:headerEnd + 2 - offset freeWhenDone:NO];
+				currentHeader = [[MultipartMessageHeader alloc] initWithData:headerData formEncoding:formEncoding];
+
+				if( nil == currentHeader ) {
+					// we've found the data is in wrong format.
+					HTTPLogError(@"MultipartFormDataParser: MultipartFormDataParser: wrong input format, coulnd't get a valid header");
+					return NO;
+				}
+                if( [delegate respondsToSelector:@selector(processStartOfPartWithHeader:)] ) {
+                    [delegate processStartOfPartWithHeader:currentHeader];
+                }
+
+				HTTPLogVerbose(@"MultipartFormDataParser: MultipartFormDataParser: Retrieved part header.");
+			}
+			// skip the two trailing \r\n, in addition to the whole header.
+			offset = headerEnd + 4;	
+		}
+		// after we've got the header, we try to
+		// find the boundary in the data.
+		int contentEnd = [self findContentEnd:workingData fromOffset:offset];
+		
+		if( contentEnd == -1 ) {
+
+			// this case, we didn't find the boundary, so the data is related to the current part.
+			// we leave the sizeToLeavePending amount of bytes to make sure we don't include 
+			// boundary part in processed data.
+			NSUInteger sizeToPass = workingData.length - offset - sizeToLeavePending;
+
+			// if we parse BASE64 encoded data, or Quoted-Printable data, we will make sure we don't break the format
+			int leaveTrailing = [self numberOfBytesToLeavePendingWithData:data length:sizeToPass encoding:currentEncoding];
+			sizeToPass -= leaveTrailing;
+			
+			if( sizeToPass <= 0 ) {
+				// wait for more data!
+				if( offset ) {
+					[pendingData setData:[NSData dataWithBytes:(char*) workingData.bytes + offset length:workingData.length - offset]];
+				}
+				return YES;
+			}
+			// decode the chunk and let the delegate use it (store in a file, for example)
+			NSData* decodedData = [MultipartFormDataParser decodedDataFromData:[NSData dataWithBytesNoCopy:(char*)workingData.bytes + offset length:workingData.length - offset - sizeToLeavePending freeWhenDone:NO] encoding:currentEncoding];
+			
+			if( [delegate respondsToSelector:@selector(processContent:WithHeader:)] ) {
+				HTTPLogVerbose(@"MultipartFormDataParser: Processed %"FMTNSINT" bytes of body",sizeToPass);
+
+				[delegate processContent: decodedData WithHeader:currentHeader];
+			}
+
+			// store the unprocessed data till the next chunks come.
+			[pendingData setData:[NSData dataWithBytes:(char*)workingData.bytes + workingData.length - sizeToLeavePending length:sizeToLeavePending]];
+			return YES;
+		}
+		else {
+
+			// Here we found the boundary.
+			// let the delegate process it, and continue going to the next parts.
+			if( [delegate respondsToSelector:@selector(processContent:WithHeader:)] ) {
+				[delegate processContent:[NSData dataWithBytesNoCopy:(char*) workingData.bytes + offset length:contentEnd - offset freeWhenDone:NO] WithHeader:currentHeader];
+			}
+
+			if( [delegate respondsToSelector:@selector(processEndOfPartWithHeader:)] ){
+				[delegate processEndOfPartWithHeader:currentHeader];
+				HTTPLogVerbose(@"MultipartFormDataParser: End of body part");
+			}
+			currentHeader = nil;
+
+			// set up offset to continue with the remaining data (if any)
+            // cast to int because above comment suggests a small number
+			offset = contentEnd + (int)boundaryData.length;
+			checkForContentEnd = YES;
+			// setting the flag tells the parser to skip all the data till CRLF
+		}
+	}
+    return YES;
+}
+
+
+//-----------------------------------------------------------------
+#pragma mark private methods
+
+- (int) offsetTillNewlineSinceOffset:(int) offset inData:(NSData*) data {
+	char* bytes = (char*) data.bytes;
+	NSUInteger length = data.length;
+	if( offset >= length - 1 ) 
+		return -1;
+
+	while ( *(uint16_t*)(bytes + offset) != 0x0A0D ) {
+		// find the trailing \r\n after the boundary. The boundary line might have any number of whitespaces before CRLF, according to rfc2046
+
+		// in debug, we might also want to know, if the file is somehow misformatted.
+#ifdef DEBUG
+		if( !isspace(*(bytes+offset)) ) {
+			HTTPLogWarn(@"MultipartFormDataParser: Warning, non-whitespace character '%c' between boundary bytes and CRLF in boundary line",*(bytes+offset) );
+		}
+		if( !isspace(*(bytes+offset+1)) ) {
+			HTTPLogWarn(@"MultipartFormDataParser: Warning, non-whitespace character '%c' between boundary bytes and CRLF in boundary line",*(bytes+offset+1) );
+		}
+#endif
+		offset++;
+		if( offset >= length ) {
+			// no endl found within current data
+			return -1;
+		}
+	}
+
+	offset += 2;
+	return offset;
+}
+
+
+- (int) processPreamble:(NSData*) data {
+	int offset = 0;
+	
+	char* boundaryBytes = (char*) boundaryData.bytes + 2; // the first boundary won't have CRLF preceding.
+    char* dataBytes = (char*) data.bytes;
+    NSUInteger boundaryLength = boundaryData.length - 2;
+    NSUInteger dataLength = data.length;
+    
+	// find the boundary without leading CRLF.
+    while( offset < dataLength - boundaryLength +1 ) {
+        int i;
+        for( i = 0;i < boundaryLength; i++ ) {
+            if( boundaryBytes[i] != dataBytes[offset + i] )
+                break;
+        }
+        if( i == boundaryLength ) {
+            break;
+        }
+		offset++;
+    }
+ 	
+	if( offset == dataLength ) {
+		// the end of preamble wasn't found in this chunk
+		NSUInteger sizeToProcess = dataLength - boundaryLength;
+		if( sizeToProcess > 0) {
+			if( [delegate respondsToSelector:@selector(processPreambleData:)] ) {
+				NSData* preambleData = [NSData dataWithBytesNoCopy: (char*) data.bytes length: data.length - offset - boundaryLength freeWhenDone:NO];
+				[delegate processPreambleData:preambleData];
+				HTTPLogVerbose(@"MultipartFormDataParser: processed preamble");
+			}
+			pendingData = [NSMutableData dataWithBytes: data.bytes + data.length - boundaryLength length:boundaryLength];
+		}
+		return -1;
+	}
+	else {
+		if ( offset && [delegate respondsToSelector:@selector(processPreambleData:)] ) {
+			NSData* preambleData = [NSData dataWithBytesNoCopy: (char*) data.bytes length: offset freeWhenDone:NO];
+			[delegate processPreambleData:preambleData];
+		}
+		offset +=boundaryLength;
+		// tells to skip CRLF after the boundary.
+		processedPreamble = YES;
+		waitingForCRLF = YES;
+	}
+	return offset;
+}
+
+
+
+- (int) findHeaderEnd:(NSData*) workingData fromOffset:(int)offset {
+    char* bytes = (char*) workingData.bytes; 
+    NSUInteger inputLength = workingData.length;
+    uint16_t separatorBytes = 0x0A0D;
+
+	while( true ) {
+		if(inputLength < offset + 3 ) {
+			// wait for more data
+			return -1;
+		}
+        if( (*((uint16_t*) (bytes+offset)) == separatorBytes) && (*((uint16_t*) (bytes+offset)+1) == separatorBytes) ) {
+			return offset;
+        }
+        offset++;
+    }
+    return -1;
+}
+
+
+- (int) findContentEnd:(NSData*) data fromOffset:(int) offset {
+    char* boundaryBytes = (char*) boundaryData.bytes;
+    char* dataBytes = (char*) data.bytes;
+    NSUInteger boundaryLength = boundaryData.length;
+    NSUInteger dataLength = data.length;
+    
+    while( offset < dataLength - boundaryLength +1 ) {
+        int i;
+        for( i = 0;i < boundaryLength; i++ ) {
+            if( boundaryBytes[i] != dataBytes[offset + i] )
+                break;
+        }
+        if( i == boundaryLength ) {
+            return offset;
+        }
+		offset++;
+    }
+    return -1;
+}
+
+
+- (int) numberOfBytesToLeavePendingWithData:(NSData*) data length:(int) length encoding:(int) encoding {
+	// If we have BASE64 or Quoted-Printable encoded data, we have to be sure
+	// we don't break the format.
+	int sizeToLeavePending = 0;
+	
+	if( encoding == contentTransferEncoding_base64 ) {	
+		char* bytes = (char*) data.bytes;
+		int i;
+		for( i = length - 1; i > 0; i++ ) {
+			if( * (uint16_t*) (bytes + i) == 0x0A0D ) {
+				break;
+			}
+		}
+		// now we've got to be sure that the length of passed data since last line
+		// is multiplier of 4.
+		sizeToLeavePending = (length - i) & ~0x11; // size to leave pending = length-i - (length-i) %4;
+		return sizeToLeavePending;
+	}
+	
+	if( encoding == contentTransferEncoding_quotedPrintable ) {
+		// we don't pass more less then 3 bytes anyway.
+		if( length <= 2 ) 
+			return length;
+		// check the last bytes to be start of encoded symbol.
+		const char* bytes = data.bytes + length - 2;
+		if( bytes[0] == '=' )
+			return 2;
+		if( bytes[1] == '=' )
+			return 1;
+		return 0;
+	}
+	return 0;
+}
+
+
+//-----------------------------------------------------------------
+#pragma mark decoding
+
+
++ (NSData*) decodedDataFromData:(NSData*) data encoding:(int) encoding {
+	switch (encoding) {
+		case contentTransferEncoding_base64: {
+			return [data base64Decoded]; 
+		} break;
+
+		case contentTransferEncoding_quotedPrintable: {
+			return [self decodedDataFromQuotedPrintableData:data];
+		} break;
+
+		default: {
+			return data;
+		} break;
+	}
+}
+
+
++ (NSData*) decodedDataFromQuotedPrintableData:(NSData *)data {
+//	http://tools.ietf.org/html/rfc2045#section-6.7
+
+	const char hex []  = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', };
+
+	NSMutableData* result = [[NSMutableData alloc] initWithLength:data.length];
+	const char* bytes = (const char*) data.bytes;
+	int count = 0;
+	NSUInteger length = data.length;
+	while( count < length ) {
+		if( bytes[count] == '=' ) {
+			[result appendBytes:bytes length:count];
+			bytes = bytes + count + 1;
+			length -= count + 1;
+			count = 0;
+
+			if( length < 3 ) {
+				HTTPLogWarn(@"MultipartFormDataParser: warning, trailing '=' in quoted printable data");
+			}
+			// soft newline
+			if( bytes[0] == '\r' ) {
+				bytes += 1;
+				if(bytes[1] == '\n' ) {
+					bytes += 2;
+				}
+				continue;
+			}
+			char encodedByte = 0;
+
+			for( int i = 0; i < sizeof(hex); i++ ) {
+				if( hex[i] == bytes[0] ) {
+					encodedByte += i << 4;
+				}
+				if( hex[i] == bytes[1] ) {
+					encodedByte += i;
+				}
+			}
+			[result appendBytes:&encodedByte length:1];
+			bytes += 2;
+		}
+
+#ifdef DEBUG
+		if( (unsigned char) bytes[count] > 126 ) {
+			HTTPLogWarn(@"MultipartFormDataParser: Warning, character with code above 126 appears in quoted printable encoded data");
+		}
+#endif
+		
+		count++;
+	}
+	return result;
+}
+
+
+@end

+ 33 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h

@@ -0,0 +1,33 @@
+//
+//  MultipartMessagePart.h
+//  HttpServer
+//
+//  Created by Валерий Гаврилов on 29.03.12.
+//  Copyright (c) 2012 LLC "Online Publishing Partners" (onlinepp.ru). All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+//-----------------------------------------------------------------
+// interface MultipartMessageHeader
+//-----------------------------------------------------------------
+enum {
+    contentTransferEncoding_unknown,
+    contentTransferEncoding_7bit,
+    contentTransferEncoding_8bit,
+    contentTransferEncoding_binary,
+    contentTransferEncoding_base64,
+    contentTransferEncoding_quotedPrintable,    
+};
+
+@interface MultipartMessageHeader : NSObject {
+    NSMutableDictionary*                    fields;
+    int                                     encoding;
+    NSString*                               contentDispositionName;
+}
+@property (strong,readonly) NSDictionary* fields;
+@property (readonly) int encoding;
+
+- (id) initWithData:(NSData*) data formEncoding:(NSStringEncoding) encoding;
+@end

+ 86 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m

@@ -0,0 +1,86 @@
+//
+//  MultipartMessagePart.m
+//  HttpServer
+//
+//  Created by Валерий Гаврилов on 29.03.12.
+//  Copyright (c) 2012 LLC "Online Publishing Partners" (onlinepp.ru). All rights reserved.
+
+#import "MultipartMessageHeader.h"
+#import "MultipartMessageHeaderField.h"
+
+#import "HTTPLogging.h"
+
+//-----------------------------------------------------------------
+#pragma mark log level
+
+#ifdef DEBUG
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#else
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#endif
+
+//-----------------------------------------------------------------
+// implementation MultipartMessageHeader
+//-----------------------------------------------------------------
+
+
+@implementation MultipartMessageHeader
+@synthesize fields,encoding;
+
+
+- (id) initWithData:(NSData *)data formEncoding:(NSStringEncoding) formEncoding {
+	if( nil == (self = [super init]) ) {
+        return self;
+    }
+	
+	fields = [[NSMutableDictionary alloc] initWithCapacity:1];
+
+	// In case encoding is not mentioned,
+	encoding = contentTransferEncoding_unknown;
+
+	char* bytes = (char*)data.bytes;
+	NSUInteger length = data.length;
+	int offset = 0;
+
+	// split header into header fields, separated by \r\n
+	uint16_t fields_separator = 0x0A0D; // \r\n
+	while( offset < length - 2 ) {
+
+		// the !isspace condition is to support header unfolding
+		if( (*(uint16_t*) (bytes+offset)  == fields_separator) && ((offset == length - 2) || !(isspace(bytes[offset+2])) )) {
+			NSData* fieldData = [NSData dataWithBytesNoCopy:bytes length:offset freeWhenDone:NO];
+			MultipartMessageHeaderField* field = [[MultipartMessageHeaderField alloc] initWithData: fieldData  contentEncoding:formEncoding];
+			if( field ) {
+				[fields setObject:field forKey:field.name];
+				HTTPLogVerbose(@"MultipartFormDataParser: Processed Header field '%@'",field.name);
+			}
+			else {
+				NSString* fieldStr = [[NSString  alloc] initWithData:fieldData encoding:NSASCIIStringEncoding];
+				HTTPLogWarn(@"MultipartFormDataParser: Failed to parse MIME header field. Input ASCII string:%@",fieldStr);
+			}
+
+			// move to the next header field
+			bytes += offset + 2;
+			length -= offset + 2;
+			offset = 0;
+			continue;
+		}
+		++ offset;
+	}
+	
+	if( !fields.count ) {
+		// it was an empty header.
+		// we have to set default values.
+		// default header.
+		[fields setObject:@"text/plain" forKey:@"Content-Type"];
+	}
+
+	return self;
+}
+
+- (NSString *)description {	
+	return [NSString stringWithFormat:@"%@",fields];
+}
+
+
+@end

+ 23 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h

@@ -0,0 +1,23 @@
+
+#import <Foundation/Foundation.h>
+
+//-----------------------------------------------------------------
+// interface MultipartMessageHeaderField
+//-----------------------------------------------------------------
+
+@interface MultipartMessageHeaderField : NSObject {
+	NSString*						name;
+    NSString*						value;
+    NSMutableDictionary*			params;
+}
+
+@property (strong, readonly) NSString*		value;
+@property (strong, readonly) NSDictionary*	params;
+@property (strong, readonly) NSString*		name;
+
+//- (id) initWithLine:(NSString*) line;
+//- (id) initWithName:(NSString*) paramName value:(NSString*) paramValue;
+
+- (id) initWithData:(NSData*) data contentEncoding:(NSStringEncoding) encoding;
+
+@end

+ 211 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m

@@ -0,0 +1,211 @@
+
+#import "MultipartMessageHeaderField.h"
+#import "HTTPLogging.h"
+
+//-----------------------------------------------------------------
+#pragma mark log level
+
+#ifdef DEBUG
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#else
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+#endif
+
+
+// helpers
+int findChar(const char* str,NSUInteger length, char c);
+NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding);
+
+//-----------------------------------------------------------------
+// interface MultipartMessageHeaderField (private)
+//-----------------------------------------------------------------
+
+
+@interface MultipartMessageHeaderField (private)
+-(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding;
+@end
+
+
+//-----------------------------------------------------------------
+// implementation MultipartMessageHeaderField
+//-----------------------------------------------------------------
+
+@implementation MultipartMessageHeaderField
+@synthesize name,value,params;
+
+- (id) initWithData:(NSData *)data contentEncoding:(NSStringEncoding)encoding {
+	params = [[NSMutableDictionary alloc] initWithCapacity:1];
+
+	char* bytes = (char*)data.bytes;
+	NSUInteger length = data.length;
+
+	int separatorOffset = findChar(bytes, length, ':');
+	if( (-1 == separatorOffset) || (separatorOffset >= length-2) ) {
+		HTTPLogError(@"MultipartFormDataParser: Bad format.No colon in field header.");
+		// tear down
+		return nil;
+	}
+	
+	// header name is always ascii encoded;
+	name = [[NSString alloc] initWithBytes: bytes length: separatorOffset encoding: NSASCIIStringEncoding];
+	if( nil == name ) {
+		HTTPLogError(@"MultipartFormDataParser: Bad MIME header name.");
+		// tear down
+		return nil;		
+	}
+	
+	// skip the separator and the next ' ' symbol
+	bytes += separatorOffset + 2;
+	length -= separatorOffset + 2;
+
+	separatorOffset = findChar(bytes, length, ';');
+	if( separatorOffset == -1 ) {
+		// couldn't find ';', means we don't have extra params here. 
+		value = [[NSString alloc] initWithBytes:bytes length: length encoding:encoding];
+
+		if( nil == value ) {
+			HTTPLogError(@"MultipartFormDataParser: Bad MIME header value for header name: '%@'",name);
+			// tear down
+			return nil;		
+		}
+		return self;
+	}
+	
+	value = [[NSString alloc] initWithBytes:bytes length: separatorOffset encoding:encoding];
+	HTTPLogVerbose(@"MultipartFormDataParser: Processing  header field '%@' : '%@'",name,value);
+	// skipe the separator and the next ' ' symbol
+	bytes += separatorOffset + 2;
+	length -= separatorOffset + 2;
+
+	// parse the "params" part of the header
+	if( ![self parseHeaderValueBytes:bytes length:length encoding:encoding] ) {
+		NSString* paramsStr = [[NSString alloc] initWithBytes:bytes length:length encoding:NSASCIIStringEncoding];
+		HTTPLogError(@"MultipartFormDataParser: Bad params for header with name '%@' and value '%@'",name,value);
+		HTTPLogError(@"MultipartFormDataParser: Params str: %@",paramsStr);
+
+		return nil;		
+	}
+	return self;
+}
+
+-(BOOL) parseHeaderValueBytes:(char*) bytes length:(NSUInteger) length encoding:(NSStringEncoding) encoding {
+	int offset = 0;
+	NSString* currentParam = nil;
+	BOOL insideQuote = NO;
+	while( offset < length ) {
+		if( bytes[offset] == '\"' ) {
+			if( !offset || bytes[offset-1] != '\\' ) {
+			   insideQuote = !insideQuote;
+			}
+		}
+
+		// skip quoted symbols
+		if( insideQuote ) {
+			++ offset;
+			continue; 
+		}
+		if( bytes[offset] == '=' ) {
+			if( currentParam ) {
+				// found '=' before terminating previous param.
+				return NO;
+			}
+			currentParam = [[NSString alloc] initWithBytes:bytes length:offset encoding:NSASCIIStringEncoding];
+
+			bytes+=offset + 1;
+			length -= offset + 1;
+			offset = 0;
+			continue;
+		}
+		if( bytes[offset] == ';' ) {
+			if( !currentParam ) {
+				// found ; before stating '='.
+				HTTPLogError(@"MultipartFormDataParser: Unexpected ';' when parsing header");
+				return NO;
+			}
+			NSString* paramValue = extractParamValue(bytes, offset,encoding);
+			 if( nil == paramValue ) {
+				HTTPLogWarn(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
+			}
+			else {
+#ifdef DEBUG
+				if( [params objectForKey:currentParam] ) {
+					HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in header %@",currentParam,name);
+				}
+#endif
+				[params setObject:paramValue forKey:currentParam];
+				HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
+			}
+
+			currentParam = nil;
+
+			// ';' separator has ' ' following, skip them.
+			bytes+=offset + 2;
+			length -= offset + 2;
+			offset = 0;
+		}
+		++ offset;
+	}
+
+	// add last param
+	if( insideQuote ) {
+		HTTPLogWarn(@"MultipartFormDataParser: unterminated quote in header %@",name);
+//		return YES;
+	}
+	if( currentParam ) {
+		NSString* paramValue = extractParamValue(bytes, length, encoding);
+
+		if( nil == paramValue ) {
+			HTTPLogError(@"MultipartFormDataParser: Failed to exctract paramValue for key %@ in header %@",currentParam,name);
+		}
+
+#ifdef DEBUG
+		if( [params objectForKey:currentParam] ) {
+			HTTPLogWarn(@"MultipartFormDataParser: param %@ mentioned more then once in one header",currentParam);
+		}
+#endif
+		[params setObject:paramValue forKey:currentParam];
+		HTTPLogVerbose(@"MultipartFormDataParser: header param: %@ = %@",currentParam,paramValue);
+		currentParam = nil;
+	}
+	
+	return YES;
+}
+
+- (NSString *)description {
+	return [NSString stringWithFormat:@"%@:%@\n params: %@",name,value,params];
+}
+
+@end
+
+int findChar(const char* str, NSUInteger length, char c) {
+	int offset = 0;
+	while( offset < length ) {
+		if( str[offset] == c )
+			return offset;
+		++ offset;
+	}
+	return -1;
+}
+
+NSString* extractParamValue(const char* bytes, NSUInteger length, NSStringEncoding encoding) {
+	if( !length ) 
+		return nil;
+	NSMutableString* value = nil;
+	
+	if( bytes[0] == '"' ) {
+		// values may be quoted. Strip the quotes to get what we need.
+		value = [[NSMutableString alloc] initWithBytes:bytes + 1 length: length - 2 encoding:encoding]; 
+	}
+	else {
+		value = [[NSMutableString alloc] initWithBytes:bytes length: length encoding:encoding];
+	}
+	// restore escaped symbols
+	NSRange range= [value rangeOfString:@"\\"];
+	while ( range.length ) {
+		[value deleteCharactersInRange:range];
+		range.location ++;
+		range = [value rangeOfString:@"\\" options:NSLiteralSearch range: range];
+	}
+	return value;
+}
+

+ 75 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h

@@ -0,0 +1,75 @@
+#import <Foundation/Foundation.h>
+#import "HTTPResponse.h"
+
+@class HTTPConnection;
+
+/**
+ * This is an asynchronous version of HTTPFileResponse.
+ * It reads data from the given file asynchronously via GCD.
+ * 
+ * It may be overriden to allow custom post-processing of the data that has been read from the file.
+ * An example of this is the HTTPDynamicFileResponse class.
+**/
+
+@interface HTTPAsyncFileResponse : NSObject <HTTPResponse>
+{	
+	HTTPConnection *connection;
+	
+	NSString *filePath;
+	UInt64 fileLength;
+	UInt64 fileOffset;  // File offset as pertains to data given to connection
+	UInt64 readOffset;  // File offset as pertains to data read from file (but maybe not returned to connection)
+	
+	BOOL aborted;
+	
+	NSData *data;
+	
+	int fileFD;
+	void *readBuffer;
+	NSUInteger readBufferSize;     // Malloced size of readBuffer
+	NSUInteger readBufferOffset;   // Offset within readBuffer where the end of existing data is
+	NSUInteger readRequestLength;
+	dispatch_queue_t readQueue;
+	dispatch_source_t readSource;
+	BOOL readSourceSuspended;
+}
+
+- (id)initWithFilePath:(NSString *)filePath forConnection:(HTTPConnection *)connection;
+- (NSString *)filePath;
+
+@end
+
+/**
+ * Explanation of Variables (excluding those that are obvious)
+ * 
+ * fileOffset
+ *   This is the number of bytes that have been returned to the connection via the readDataOfLength method.
+ *   If 1KB of data has been read from the file, but none of that data has yet been returned to the connection,
+ *   then the fileOffset variable remains at zero.
+ *   This variable is used in the calculation of the isDone method.
+ *   Only after all data has been returned to the connection are we actually done.
+ * 
+ * readOffset
+ *   Represents the offset of the file descriptor.
+ *   In other words, the file position indidcator for our read stream.
+ *   It might be easy to think of it as the total number of bytes that have been read from the file.
+ *   However, this isn't entirely accurate, as the setOffset: method may have caused us to
+ *   jump ahead in the file (lseek).
+ * 
+ * readBuffer
+ *   Malloc'd buffer to hold data read from the file.
+ * 
+ * readBufferSize
+ *   Total allocation size of malloc'd buffer.
+ * 
+ * readBufferOffset
+ *   Represents the position in the readBuffer where we should store new bytes.
+ * 
+ * readRequestLength
+ *   The total number of bytes that were requested from the connection.
+ *   It's OK if we return a lesser number of bytes to the connection.
+ *   It's NOT OK if we return a greater number of bytes to the connection.
+ *   Doing so would disrupt proper support for range requests.
+ *   If, however, the response is chunked then we don't need to worry about this.
+ *   Chunked responses inheritly don't support range requests.
+**/

+ 405 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.m

@@ -0,0 +1,405 @@
+#import "HTTPAsyncFileResponse.h"
+#import "HTTPConnection.h"
+#import "HTTPLogging.h"
+
+#import <unistd.h>
+#import <fcntl.h>
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels : off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
+
+#define NULL_FD  -1
+
+/**
+ * Architecure overview:
+ * 
+ * HTTPConnection will invoke our readDataOfLength: method to fetch data.
+ * We will return nil, and then proceed to read the data via our readSource on our readQueue.
+ * Once the requested amount of data has been read, we then pause our readSource,
+ * and inform the connection of the available data.
+ * 
+ * While our read is in progress, we don't have to worry about the connection calling any other methods,
+ * except the connectionDidClose method, which would be invoked if the remote end closed the socket connection.
+ * To safely handle this, we do a synchronous dispatch on the readQueue,
+ * and nilify the connection as well as cancel our readSource.
+ * 
+ * In order to minimize resource consumption during a HEAD request,
+ * we don't open the file until we have to (until the connection starts requesting data).
+**/
+
+@implementation HTTPAsyncFileResponse
+
+- (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent
+{
+	if ((self = [super init]))
+	{
+		HTTPLogTrace();
+		
+		connection = parent; // Parents retain children, children do NOT retain parents
+		
+		fileFD = NULL_FD;
+		filePath = [fpath copy];
+		if (filePath == nil)
+		{
+			HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE);
+			
+			return nil;
+		}
+		
+		NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL];
+		if (fileAttributes == nil)
+		{
+			HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath);
+			
+			return nil;
+		}
+		
+		fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
+		fileOffset = 0;
+		
+		aborted = NO;
+		
+		// We don't bother opening the file here.
+		// If this is a HEAD request we only need to know the fileLength.
+	}
+	return self;
+}
+
+- (void)abort
+{
+	HTTPLogTrace();
+	
+	[connection responseDidAbort:self];
+	aborted = YES;
+}
+
+- (void)processReadBuffer
+{
+	// This method is here to allow superclasses to perform post-processing of the data.
+	// For an example, see the HTTPDynamicFileResponse class.
+	// 
+	// At this point, the readBuffer has readBufferOffset bytes available.
+	// This method is in charge of updating the readBufferOffset.
+	// Failure to do so will cause the readBuffer to grow to fileLength. (Imagine a 1 GB file...)
+	
+	// Copy the data out of the temporary readBuffer.
+	data = [[NSData alloc] initWithBytes:readBuffer length:readBufferOffset];
+	
+	// Reset the read buffer.
+	readBufferOffset = 0;
+	
+	// Notify the connection that we have data available for it.
+	[connection responseHasAvailableData:self];
+}
+
+- (void)pauseReadSource
+{
+	if (!readSourceSuspended)
+	{
+		HTTPLogVerbose(@"%@[%p]: Suspending readSource", THIS_FILE, self);
+		
+		readSourceSuspended = YES;
+		dispatch_suspend(readSource);
+	}
+}
+
+- (void)resumeReadSource
+{
+	if (readSourceSuspended)
+	{
+		HTTPLogVerbose(@"%@[%p]: Resuming readSource", THIS_FILE, self);
+		
+		readSourceSuspended = NO;
+		dispatch_resume(readSource);
+	}
+}
+
+- (void)cancelReadSource
+{
+	HTTPLogVerbose(@"%@[%p]: Canceling readSource", THIS_FILE, self);
+	
+	dispatch_source_cancel(readSource);
+	
+	// Cancelling a dispatch source doesn't
+	// invoke the cancel handler if the dispatch source is paused.
+	
+	if (readSourceSuspended)
+	{
+		readSourceSuspended = NO;
+		dispatch_resume(readSource);
+	}
+}
+
+- (BOOL)openFileAndSetupReadSource
+{
+	HTTPLogTrace();
+	
+	fileFD = open([filePath UTF8String], (O_RDONLY | O_NONBLOCK));
+	if (fileFD == NULL_FD)
+	{
+		HTTPLogError(@"%@: Unable to open file. filePath: %@", THIS_FILE, filePath);
+		
+		return NO;
+	}
+	
+	HTTPLogVerbose(@"%@[%p]: Open fd[%i] -> %@", THIS_FILE, self, fileFD, filePath);
+	
+	readQueue = dispatch_queue_create("HTTPAsyncFileResponse", NULL);
+	readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fileFD, 0, readQueue);
+	
+	
+	dispatch_source_set_event_handler(readSource, ^{
+		
+		HTTPLogTrace2(@"%@: eventBlock - fd[%i]", THIS_FILE, fileFD);
+		
+		// Determine how much data we should read.
+		// 
+		// It is OK if we ask to read more bytes than exist in the file.
+		// It is NOT OK to over-allocate the buffer.
+		
+		unsigned long long _bytesAvailableOnFD = dispatch_source_get_data(readSource);
+		
+		UInt64 _bytesLeftInFile = fileLength - readOffset;
+		
+		NSUInteger bytesAvailableOnFD;
+		NSUInteger bytesLeftInFile;
+		
+		bytesAvailableOnFD = (_bytesAvailableOnFD > NSUIntegerMax) ? NSUIntegerMax : (NSUInteger)_bytesAvailableOnFD;
+		bytesLeftInFile    = (_bytesLeftInFile    > NSUIntegerMax) ? NSUIntegerMax : (NSUInteger)_bytesLeftInFile;
+		
+		NSUInteger bytesLeftInRequest = readRequestLength - readBufferOffset;
+		
+		NSUInteger bytesLeft = MIN(bytesLeftInRequest, bytesLeftInFile);
+		
+		NSUInteger bytesToRead = MIN(bytesAvailableOnFD, bytesLeft);
+		
+		// Make sure buffer is big enough for read request.
+		// Do not over-allocate.
+		
+		if (readBuffer == NULL || bytesToRead > (readBufferSize - readBufferOffset))
+		{
+			readBufferSize = bytesToRead;
+			readBuffer = reallocf(readBuffer, (size_t)bytesToRead);
+			
+			if (readBuffer == NULL)
+			{
+				HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self);
+				
+				[self pauseReadSource];
+				[self abort];
+				
+				return;
+			}
+		}
+		
+		// Perform the read
+		
+		HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, (unsigned long)bytesToRead);
+		
+		ssize_t result = read(fileFD, readBuffer + readBufferOffset, (size_t)bytesToRead);
+		
+		// Check the results
+		if (result < 0)
+		{
+			HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath);
+			
+			[self pauseReadSource];
+			[self abort];
+		}
+		else if (result == 0)
+		{
+			HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath);
+			
+			[self pauseReadSource];
+			[self abort];
+		}
+		else // (result > 0)
+		{
+			HTTPLogVerbose(@"%@[%p]: Read %lu bytes from file", THIS_FILE, self, (unsigned long)result);
+			
+			readOffset += result;
+			readBufferOffset += result;
+			
+			[self pauseReadSource];
+			[self processReadBuffer];
+		}
+		
+	});
+	
+	int theFileFD = fileFD;
+	#if !OS_OBJECT_USE_OBJC
+	dispatch_source_t theReadSource = readSource;
+	#endif
+	
+	dispatch_source_set_cancel_handler(readSource, ^{
+		
+		// Do not access self from within this block in any way, shape or form.
+		// 
+		// Note: You access self if you reference an iVar.
+		
+		HTTPLogTrace2(@"%@: cancelBlock - Close fd[%i]", THIS_FILE, theFileFD);
+		
+		#if !OS_OBJECT_USE_OBJC
+		dispatch_release(theReadSource);
+		#endif
+		close(theFileFD);
+	});
+	
+	readSourceSuspended = YES;
+	
+	return YES;
+}
+
+- (BOOL)openFileIfNeeded
+{
+	if (aborted)
+	{
+		// The file operation has been aborted.
+		// This could be because we failed to open the file,
+		// or the reading process failed.
+		return NO;
+	}
+	
+	if (fileFD != NULL_FD)
+	{
+		// File has already been opened.
+		return YES;
+	}
+	
+	return [self openFileAndSetupReadSource];
+}	
+
+- (UInt64)contentLength
+{
+	HTTPLogTrace2(@"%@[%p]: contentLength - %llu", THIS_FILE, self, fileLength);
+	
+	return fileLength;
+}
+
+- (UInt64)offset
+{
+	HTTPLogTrace();
+	
+	return fileOffset;
+}
+
+- (void)setOffset:(UInt64)offset
+{
+	HTTPLogTrace2(@"%@[%p]: setOffset:%llu", THIS_FILE, self, offset);
+	
+	if (![self openFileIfNeeded])
+	{
+		// File opening failed,
+		// or response has been aborted due to another error.
+		return;
+	}
+	
+	fileOffset = offset;
+	readOffset = offset;
+	
+	off_t result = lseek(fileFD, (off_t)offset, SEEK_SET);
+	if (result == -1)
+	{
+		HTTPLogError(@"%@[%p]: lseek failed - errno(%i) filePath(%@)", THIS_FILE, self, errno, filePath);
+		
+		[self abort];
+	}
+}
+
+- (NSData *)readDataOfLength:(NSUInteger)length
+{
+	HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length);
+	
+	if (data)
+	{
+		NSUInteger dataLength = [data length];
+		
+		HTTPLogVerbose(@"%@[%p]: Returning data of length %lu", THIS_FILE, self, (unsigned long)dataLength);
+		
+		fileOffset += dataLength;
+		
+		NSData *result = data;
+		data = nil;
+		
+		return result;
+	}
+	else
+	{
+		if (![self openFileIfNeeded])
+		{
+			// File opening failed,
+			// or response has been aborted due to another error.
+			return nil;
+		}
+		
+		dispatch_sync(readQueue, ^{
+			
+			NSAssert(readSourceSuspended, @"Invalid logic - perhaps HTTPConnection has changed.");
+			
+			readRequestLength = length;
+			[self resumeReadSource];
+		});
+		
+		return nil;
+	}
+}
+
+- (BOOL)isDone
+{
+	BOOL result = (fileOffset == fileLength);
+	
+	HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
+	
+	return result;
+}
+
+- (NSString *)filePath
+{
+	return filePath;
+}
+
+- (BOOL)isAsynchronous
+{
+	HTTPLogTrace();
+	
+	return YES;
+}
+
+- (void)connectionDidClose
+{
+	HTTPLogTrace();
+	
+	if (fileFD != NULL_FD)
+	{
+		dispatch_sync(readQueue, ^{
+			
+			// Prevent any further calls to the connection
+			connection = nil;
+			
+			// Cancel the readSource.
+			// We do this here because the readSource's eventBlock has retained self.
+			// In other words, if we don't cancel the readSource, we will never get deallocated.
+			
+			[self cancelReadSource];
+		});
+	}
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+	#if !OS_OBJECT_USE_OBJC
+	if (readQueue) dispatch_release(readQueue);
+	#endif
+	
+	if (readBuffer)
+		free(readBuffer);
+}
+
+@end

+ 13 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h

@@ -0,0 +1,13 @@
+#import <Foundation/Foundation.h>
+#import "HTTPResponse.h"
+
+
+@interface HTTPDataResponse : NSObject <HTTPResponse>
+{
+	NSUInteger offset;
+	NSData *data;
+}
+
+- (id)initWithData:(NSData *)data;
+
+@end

+ 79 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m

@@ -0,0 +1,79 @@
+#import "HTTPDataResponse.h"
+#import "HTTPLogging.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels : off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_OFF; // | HTTP_LOG_FLAG_TRACE;
+
+
+@implementation HTTPDataResponse
+
+- (id)initWithData:(NSData *)dataParam
+{
+	if((self = [super init]))
+	{
+		HTTPLogTrace();
+		
+		offset = 0;
+		data = dataParam;
+	}
+	return self;
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+}
+
+- (UInt64)contentLength
+{
+	UInt64 result = (UInt64)[data length];
+	
+	HTTPLogTrace2(@"%@[%p]: contentLength - %llu", THIS_FILE, self, result);
+	
+	return result;
+}
+
+- (UInt64)offset
+{
+	HTTPLogTrace();
+	
+	return offset;
+}
+
+- (void)setOffset:(UInt64)offsetParam
+{
+	HTTPLogTrace2(@"%@[%p]: setOffset:%lu", THIS_FILE, self, (unsigned long)offset);
+	
+	offset = (NSUInteger)offsetParam;
+}
+
+- (NSData *)readDataOfLength:(NSUInteger)lengthParameter
+{
+	HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)lengthParameter);
+	
+	NSUInteger remaining = [data length] - offset;
+	NSUInteger length = lengthParameter < remaining ? lengthParameter : remaining;
+	
+	void *bytes = (void *)([data bytes] + offset);
+	
+	offset += length;
+	
+	return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO];
+}
+
+- (BOOL)isDone
+{
+	BOOL result = (offset == [data length]);
+	
+	HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
+	
+	return result;
+}
+
+@end

+ 52 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h

@@ -0,0 +1,52 @@
+#import <Foundation/Foundation.h>
+#import "HTTPResponse.h"
+#import "HTTPAsyncFileResponse.h"
+
+/**
+ * This class is designed to assist with dynamic content.
+ * Imagine you have a file that you want to make dynamic:
+ * 
+ * <html>
+ * <body>
+ *   <h1>ComputerName Control Panel</h1>
+ *   ...
+ *   <li>System Time: SysTime</li>
+ * </body>
+ * </html>
+ * 
+ * Now you could generate the entire file in Objective-C,
+ * but this would be a horribly tedious process.
+ * Beside, you want to design the file with professional tools to make it look pretty.
+ * 
+ * So all you have to do is escape your dynamic content like this:
+ * 
+ * ...
+ *   <h1>%%ComputerName%% Control Panel</h1>
+ * ...
+ *   <li>System Time: %%SysTime%%</li>
+ * 
+ * And then you create an instance of this class with:
+ * 
+ * - separator = @"%%"
+ * - replacementDictionary = { "ComputerName"="Black MacBook", "SysTime"="2010-04-30 03:18:24" }
+ * 
+ * This class will then perform the replacements for you, on the fly, as it reads the file data.
+ * This class is also asynchronous, so it will perform the file IO using its own GCD queue.
+ * 
+ * All keys for the replacementDictionary must be NSString's.
+ * Values for the replacementDictionary may be NSString's, or any object that
+ * returns what you want when its description method is invoked.
+**/
+
+@interface HTTPDynamicFileResponse : HTTPAsyncFileResponse
+{
+	NSData *separator;
+	NSDictionary *replacementDict;
+}
+
+- (id)initWithFilePath:(NSString *)filePath
+         forConnection:(HTTPConnection *)connection
+             separator:(NSString *)separatorStr
+ replacementDictionary:(NSDictionary *)dictionary;
+
+@end

+ 292 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.m

@@ -0,0 +1,292 @@
+#import "HTTPDynamicFileResponse.h"
+#import "HTTPConnection.h"
+#import "HTTPLogging.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels : off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
+
+#define NULL_FD  -1
+
+
+@implementation HTTPDynamicFileResponse
+
+- (id)initWithFilePath:(NSString *)fpath
+         forConnection:(HTTPConnection *)parent
+             separator:(NSString *)separatorStr
+ replacementDictionary:(NSDictionary *)dict
+{
+	if ((self = [super initWithFilePath:fpath forConnection:parent]))
+	{
+		HTTPLogTrace();
+		
+		separator = [separatorStr dataUsingEncoding:NSUTF8StringEncoding];
+		replacementDict = dict;
+	}
+	return self;
+}
+
+- (BOOL)isChunked
+{
+	HTTPLogTrace();
+	
+	return YES;
+}
+
+- (UInt64)contentLength
+{
+	// This method shouldn't be called since we're using a chunked response.
+	// We override it just to be safe.
+	
+	HTTPLogTrace();
+	
+	return 0;
+}
+
+- (void)setOffset:(UInt64)offset
+{
+	// This method shouldn't be called since we're using a chunked response.
+	// We override it just to be safe.
+	
+	HTTPLogTrace();
+}
+
+- (BOOL)isDone
+{
+	BOOL result = (readOffset == fileLength) && (readBufferOffset == 0);
+	
+	HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
+	
+	return result;
+}
+
+- (void)processReadBuffer
+{
+	HTTPLogTrace();
+	
+	// At this point, the readBuffer has readBufferOffset bytes available.
+	// This method is in charge of updating the readBufferOffset.
+	
+	NSUInteger bufLen = readBufferOffset;
+	NSUInteger sepLen = [separator length];
+	
+	// We're going to start looking for the separator at the beginning of the buffer,
+	// and stop when we get to the point where the separator would no longer fit in the buffer.
+	
+	NSUInteger offset = 0;
+	NSUInteger stopOffset = (bufLen > sepLen) ? bufLen - sepLen + 1 : 0;
+	
+	// In order to do the replacement, we need to find the starting and ending separator.
+	// For example:
+	// 
+	// %%USER_NAME%%
+	// 
+	// Where "%%" is the separator.
+	
+	BOOL found1 = NO;
+	BOOL found2 = NO;
+	
+	NSUInteger s1 = 0;
+	NSUInteger s2 = 0;
+	
+	const void *sep = [separator bytes];
+	
+	while (offset < stopOffset)
+	{
+		const void *subBuffer = readBuffer + offset;
+		
+		if (memcmp(subBuffer, sep, sepLen) == 0)
+		{
+			if (!found1)
+			{
+				// Found the first separator
+				
+				found1 = YES;
+				s1 = offset;
+				offset += sepLen;
+				
+				HTTPLogVerbose(@"%@[%p]: Found s1 at %lu", THIS_FILE, self, (unsigned long)s1);
+			}
+			else
+			{
+				// Found the second separator
+				
+				found2 = YES;
+				s2 = offset;
+				offset += sepLen;
+				
+				HTTPLogVerbose(@"%@[%p]: Found s2 at %lu", THIS_FILE, self, (unsigned long)s2);
+			}
+			
+			if (found1 && found2)
+			{
+				// We found our separators.
+				// Now extract the string between the two separators.
+				
+				NSRange fullRange = NSMakeRange(s1, (s2 - s1 + sepLen));
+				NSRange strRange = NSMakeRange(s1 + sepLen, (s2 - s1 - sepLen));
+				
+				// Wish we could use the simple subdataWithRange method.
+				// But that method copies the bytes...
+				// So for performance reasons, we need to use the methods that don't copy the bytes.
+				
+				void *strBuf = readBuffer + strRange.location;
+				NSUInteger strLen = strRange.length;
+				
+				NSString *key = [[NSString alloc] initWithBytes:strBuf length:strLen encoding:NSUTF8StringEncoding];
+				if (key)
+				{
+					// Is there a given replacement for this key?
+					
+					id value = [replacementDict objectForKey:key];
+					if (value)
+					{
+						// Found the replacement value.
+						// Now perform the replacement in the buffer.
+						
+						HTTPLogVerbose(@"%@[%p]: key(%@) -> value(%@)", THIS_FILE, self, key, value);
+						
+						NSData *v = [[value description] dataUsingEncoding:NSUTF8StringEncoding];
+						NSUInteger vLength = [v length];
+						
+						if (fullRange.length == vLength)
+						{
+							// Replacement is exactly the same size as what it is replacing
+							
+							// memcpy(void *restrict dst, const void *restrict src, size_t n);
+							
+							memcpy(readBuffer + fullRange.location, [v bytes], vLength);
+						}
+						else // (fullRange.length != vLength)
+						{
+							NSInteger diff = (NSInteger)vLength - (NSInteger)fullRange.length;
+							
+							if (diff > 0)
+							{
+								// Replacement is bigger than what it is replacing.
+								// Make sure there is room in the buffer for the replacement.
+								
+								if (diff > (readBufferSize - bufLen))
+								{
+									NSUInteger inc = MAX(diff, 256);
+									
+									readBufferSize += inc;
+									readBuffer = reallocf(readBuffer, readBufferSize);
+								}
+							}
+							
+							// Move the data that comes after the replacement.
+							// 
+							// If replacement is smaller than what it is replacing,
+							// then we are shifting the data toward the beginning of the buffer.
+							// 
+							// If replacement is bigger than what it is replacing,
+							// then we are shifting the data toward the end of the buffer.
+							// 
+							// memmove(void *dst, const void *src, size_t n);
+							// 
+							// The memmove() function copies n bytes from src to dst.
+							// The two areas may overlap; the copy is always done in a non-destructive manner.
+							
+							void *src = readBuffer + fullRange.location + fullRange.length;
+							void *dst = readBuffer + fullRange.location + vLength;
+							
+							NSUInteger remaining = bufLen - (fullRange.location + fullRange.length);
+							
+							memmove(dst, src, remaining);
+							
+							// Now copy the replacement into its location.
+							// 
+							// memcpy(void *restrict dst, const void *restrict src, size_t n)
+							// 
+							// The memcpy() function copies n bytes from src to dst.
+							// If the two areas overlap, behavior is undefined.
+							
+							memcpy(readBuffer + fullRange.location, [v bytes], vLength);
+							
+							// And don't forget to update our indices.
+							
+							bufLen     += diff;
+							offset     += diff;
+							stopOffset += diff;
+						}
+					}
+					
+				}
+				
+				found1 = found2 = NO;
+			}
+		}
+		else
+		{
+			offset++;
+		}
+	}
+	
+	// We've gone through our buffer now, and performed all the replacements that we could.
+	// It's now time to update the amount of available data we have.
+	
+	if (readOffset == fileLength)
+	{
+		// We've read in the entire file.
+		// So there can be no more replacements.
+		
+		data = [[NSData alloc] initWithBytes:readBuffer length:bufLen];
+		readBufferOffset = 0;
+	}
+	else
+	{
+		// There are a couple different situations that we need to take into account here.
+		// 
+		// Imagine the following file:
+		// My name is %%USER_NAME%%
+		// 
+		// Situation 1:
+		// The first chunk of data we read was "My name is %%".
+		// So we found the first separator, but not the second.
+		// In this case we can only return the data that precedes the first separator.
+		// 
+		// Situation 2:
+		// The first chunk of data we read was "My name is %".
+		// So we didn't find any separators, but part of a separator may be included in our buffer.
+		
+		NSUInteger available;
+		if (found1)
+		{
+			// Situation 1
+			available = s1;
+		}
+		else
+		{
+			// Situation 2
+			available = stopOffset;
+		}
+		
+		// Copy available data
+		
+		data = [[NSData alloc] initWithBytes:readBuffer length:available];
+		
+		// Remove the copied data from the buffer.
+		// We do this by shifting the remaining data toward the beginning of the buffer.
+		
+		NSUInteger remaining = bufLen - available;
+		
+		memmove(readBuffer, readBuffer + available, remaining);
+		readBufferOffset = remaining;
+	}
+	
+	[connection responseHasAvailableData:self];
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+	
+}
+
+@end

+ 9 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h

@@ -0,0 +1,9 @@
+#import "HTTPResponse.h"
+
+@interface HTTPErrorResponse : NSObject <HTTPResponse> {
+    NSInteger _status;
+}
+
+- (id)initWithErrorCode:(int)httpErrorCode;
+
+@end

+ 38 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m

@@ -0,0 +1,38 @@
+#import "HTTPErrorResponse.h"
+
+@implementation HTTPErrorResponse
+
+-(id)initWithErrorCode:(int)httpErrorCode
+{
+    if ((self = [super init]))
+    {
+        _status = httpErrorCode;
+    }
+
+    return self;
+}
+
+- (UInt64) contentLength {
+    return 0;
+}
+
+- (UInt64) offset {
+    return 0;
+}
+
+- (void)setOffset:(UInt64)offset {
+    ;
+}
+
+- (NSData*) readDataOfLength:(NSUInteger)length {
+    return nil;
+}
+
+- (BOOL) isDone {
+    return YES;
+}
+
+- (NSInteger) status {
+    return _status;
+}
+@end

+ 25 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h

@@ -0,0 +1,25 @@
+#import <Foundation/Foundation.h>
+#import "HTTPResponse.h"
+
+@class HTTPConnection;
+
+
+@interface HTTPFileResponse : NSObject <HTTPResponse>
+{
+	HTTPConnection *connection;
+	
+	NSString *filePath;
+	UInt64 fileLength;
+	UInt64 fileOffset;
+	
+	BOOL aborted;
+	
+	int fileFD;
+	void *buffer;
+	NSUInteger bufferSize;
+}
+
+- (id)initWithFilePath:(NSString *)filePath forConnection:(HTTPConnection *)connection;
+- (NSString *)filePath;
+
+@end

+ 237 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m

@@ -0,0 +1,237 @@
+#import "HTTPFileResponse.h"
+#import "HTTPConnection.h"
+#import "HTTPLogging.h"
+
+#import <unistd.h>
+#import <fcntl.h>
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels : off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
+
+#define NULL_FD  -1
+
+
+@implementation HTTPFileResponse
+
+- (id)initWithFilePath:(NSString *)fpath forConnection:(HTTPConnection *)parent
+{
+	if((self = [super init]))
+	{
+		HTTPLogTrace();
+		
+		connection = parent; // Parents retain children, children do NOT retain parents
+		
+		fileFD = NULL_FD;
+		filePath = [[fpath copy] stringByResolvingSymlinksInPath];
+		if (filePath == nil)
+		{
+			HTTPLogWarn(@"%@: Init failed - Nil filePath", THIS_FILE);
+			
+			return nil;
+		}
+		
+		NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
+		if (fileAttributes == nil)
+		{
+			HTTPLogWarn(@"%@: Init failed - Unable to get file attributes. filePath: %@", THIS_FILE, filePath);
+			
+			return nil;
+		}
+		
+		fileLength = (UInt64)[[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
+		fileOffset = 0;
+		
+		aborted = NO;
+		
+		// We don't bother opening the file here.
+		// If this is a HEAD request we only need to know the fileLength.
+	}
+	return self;
+}
+
+- (void)abort
+{
+	HTTPLogTrace();
+	
+	[connection responseDidAbort:self];
+	aborted = YES;
+}
+
+- (BOOL)openFile
+{
+	HTTPLogTrace();
+	
+	fileFD = open([filePath UTF8String], O_RDONLY);
+	if (fileFD == NULL_FD)
+	{
+		HTTPLogError(@"%@[%p]: Unable to open file. filePath: %@", THIS_FILE, self, filePath);
+		
+		[self abort];
+		return NO;
+	}
+	
+	HTTPLogVerbose(@"%@[%p]: Open fd[%i] -> %@", THIS_FILE, self, fileFD, filePath);
+	
+	return YES;
+}
+
+- (BOOL)openFileIfNeeded
+{
+	if (aborted)
+	{
+		// The file operation has been aborted.
+		// This could be because we failed to open the file,
+		// or the reading process failed.
+		return NO;
+	}
+	
+	if (fileFD != NULL_FD)
+	{
+		// File has already been opened.
+		return YES;
+	}
+	
+	return [self openFile];
+}
+
+- (UInt64)contentLength
+{
+	HTTPLogTrace();
+	
+	return fileLength;
+}
+
+- (UInt64)offset
+{
+	HTTPLogTrace();
+	
+	return fileOffset;
+}
+
+- (void)setOffset:(UInt64)offset
+{
+	HTTPLogTrace2(@"%@[%p]: setOffset:%llu", THIS_FILE, self, offset);
+	
+	if (![self openFileIfNeeded])
+	{
+		// File opening failed,
+		// or response has been aborted due to another error.
+		return;
+	}
+	
+	fileOffset = offset;
+	
+	off_t result = lseek(fileFD, (off_t)offset, SEEK_SET);
+	if (result == -1)
+	{
+		HTTPLogError(@"%@[%p]: lseek failed - errno(%i) filePath(%@)", THIS_FILE, self, errno, filePath);
+		
+		[self abort];
+	}
+}
+
+- (NSData *)readDataOfLength:(NSUInteger)length
+{
+	HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length);
+	
+	if (![self openFileIfNeeded])
+	{
+		// File opening failed,
+		// or response has been aborted due to another error.
+		return nil;
+	}
+	
+	// Determine how much data we should read.
+	// 
+	// It is OK if we ask to read more bytes than exist in the file.
+	// It is NOT OK to over-allocate the buffer.
+	
+	UInt64 bytesLeftInFile = fileLength - fileOffset;
+	
+	NSUInteger bytesToRead = (NSUInteger)MIN(length, bytesLeftInFile);
+	
+	// Make sure buffer is big enough for read request.
+	// Do not over-allocate.
+	
+	if (buffer == NULL || bufferSize < bytesToRead)
+	{
+		bufferSize = bytesToRead;
+		buffer = reallocf(buffer, (size_t)bufferSize);
+		
+		if (buffer == NULL)
+		{
+			HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self);
+			
+			[self abort];
+			return nil;
+		}
+	}
+	
+	// Perform the read
+	
+	HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, (unsigned long)bytesToRead);
+	
+	ssize_t result = read(fileFD, buffer, bytesToRead);
+	
+	// Check the results
+	
+	if (result < 0)
+	{
+		HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath);
+		
+		[self abort];
+		return nil;
+	}
+	else if (result == 0)
+	{
+		HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath);
+		
+		[self abort];
+		return nil;
+	}
+	else // (result > 0)
+	{
+		HTTPLogVerbose(@"%@[%p]: Read %ld bytes from file", THIS_FILE, self, (long)result);
+		
+		fileOffset += result;
+		
+		return [NSData dataWithBytes:buffer length:result];
+	}
+}
+
+- (BOOL)isDone
+{
+	BOOL result = (fileOffset == fileLength);
+	
+	HTTPLogTrace2(@"%@[%p]: isDone - %@", THIS_FILE, self, (result ? @"YES" : @"NO"));
+	
+	return result;
+}
+
+- (NSString *)filePath
+{
+	return filePath;
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+	if (fileFD != NULL_FD)
+	{
+		HTTPLogVerbose(@"%@[%p]: Close fd[%i]", THIS_FILE, self, fileFD);
+		
+		close(fileFD);
+	}
+	
+	if (buffer)
+		free(buffer);
+	
+}
+
+@end

+ 12 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h

@@ -0,0 +1,12 @@
+#import <Foundation/Foundation.h>
+#import "HTTPResponse.h"
+
+
+@interface HTTPRedirectResponse : NSObject <HTTPResponse>
+{
+	NSString *redirectPath;
+}
+
+- (id)initWithPath:(NSString *)redirectPath;
+
+@end

+ 73 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m

@@ -0,0 +1,73 @@
+#import "HTTPRedirectResponse.h"
+#import "HTTPLogging.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels : off, error, warn, info, verbose
+// Other flags: trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_OFF; // | HTTP_LOG_FLAG_TRACE;
+
+
+@implementation HTTPRedirectResponse
+
+- (id)initWithPath:(NSString *)path
+{
+	if ((self = [super init]))
+	{
+		HTTPLogTrace();
+		
+		redirectPath = [path copy];
+	}
+	return self;
+}
+
+- (UInt64)contentLength
+{
+	return 0;
+}
+
+- (UInt64)offset
+{
+	return 0;
+}
+
+- (void)setOffset:(UInt64)offset
+{
+	// Nothing to do
+}
+
+- (NSData *)readDataOfLength:(NSUInteger)length
+{
+	HTTPLogTrace();
+	
+	return nil;
+}
+
+- (BOOL)isDone
+{
+	return YES;
+}
+
+- (NSDictionary *)httpHeaders
+{
+	HTTPLogTrace();
+	
+	return [NSDictionary dictionaryWithObject:redirectPath forKey:@"Location"];
+}
+
+- (NSInteger)status
+{
+	HTTPLogTrace();
+	
+	return 302;
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+}
+
+@end

+ 105 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/WebSocket.h

@@ -0,0 +1,105 @@
+#import <Foundation/Foundation.h>
+
+@class HTTPMessage;
+@class GCDAsyncSocket;
+
+
+#define WebSocketDidDieNotification  @"WebSocketDidDie"
+
+@interface WebSocket : NSObject
+{
+	dispatch_queue_t websocketQueue;
+	
+	HTTPMessage *request;
+	GCDAsyncSocket *asyncSocket;
+	
+	NSData *term;
+	
+	BOOL isStarted;
+	BOOL isOpen;
+	BOOL isVersion76;
+	
+	id __unsafe_unretained delegate;
+}
+
++ (BOOL)isWebSocketRequest:(HTTPMessage *)request;
+
+- (id)initWithRequest:(HTTPMessage *)request socket:(GCDAsyncSocket *)socket;
+
+/**
+ * Delegate option.
+ * 
+ * In most cases it will be easier to subclass WebSocket,
+ * but some circumstances may lead one to prefer standard delegate callbacks instead.
+**/
+@property (/* atomic */ unsafe_unretained) id delegate;
+
+/**
+ * The WebSocket class is thread-safe, generally via it's GCD queue.
+ * All public API methods are thread-safe,
+ * and the subclass API methods are thread-safe as they are all invoked on the same GCD queue.
+**/
+@property (nonatomic, readonly) dispatch_queue_t websocketQueue;
+
+/**
+ * Public API
+ * 
+ * These methods are automatically called by the HTTPServer.
+ * You may invoke the stop method yourself to close the WebSocket manually.
+**/
+- (void)start;
+- (void)stop;
+
+/**
+ * Public API
+ *
+ * Sends a message over the WebSocket.
+ * This method is thread-safe.
+ **/
+- (void)sendMessage:(NSString *)msg;
+
+/**
+ * Public API
+ *
+ * Sends a message over the WebSocket.
+ * This method is thread-safe.
+ **/
+- (void)sendData:(NSData *)msg;
+
+/**
+ * Subclass API
+ * 
+ * These methods are designed to be overriden by subclasses.
+**/
+- (void)didOpen;
+- (void)didReceiveMessage:(NSString *)msg;
+- (void)didClose;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * There are two ways to create your own custom WebSocket:
+ * 
+ * - Subclass it and override the methods you're interested in.
+ * - Use traditional delegate paradigm along with your own custom class.
+ * 
+ * They both exist to allow for maximum flexibility.
+ * In most cases it will be easier to subclass WebSocket.
+ * However some circumstances may lead one to prefer standard delegate callbacks instead.
+ * One such example, you're already subclassing another class, so subclassing WebSocket isn't an option.
+**/
+
+@protocol WebSocketDelegate
+@optional
+
+- (void)webSocketDidOpen:(WebSocket *)ws;
+
+- (void)webSocket:(WebSocket *)ws didReceiveMessage:(NSString *)msg;
+
+- (void)webSocketDidClose:(WebSocket *)ws;
+
+@end

+ 791 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Core/WebSocket.m

@@ -0,0 +1,791 @@
+#import "WebSocket.h"
+#import "HTTPMessage.h"
+#import "GCDAsyncSocket.h"
+#import "DDNumber.h"
+#import "DDData.h"
+#import "HTTPLogging.h"
+
+#if ! __has_feature(objc_arc)
+#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+// Log levels: off, error, warn, info, verbose
+// Other flags : trace
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
+
+#define TIMEOUT_NONE          -1
+#define TIMEOUT_REQUEST_BODY  10
+
+#define TAG_HTTP_REQUEST_BODY      100
+#define TAG_HTTP_RESPONSE_HEADERS  200
+#define TAG_HTTP_RESPONSE_BODY     201
+
+#define TAG_PREFIX                 300
+#define TAG_MSG_PLUS_SUFFIX        301
+#define TAG_MSG_WITH_LENGTH        302
+#define TAG_MSG_MASKING_KEY        303
+#define TAG_PAYLOAD_PREFIX         304
+#define TAG_PAYLOAD_LENGTH         305
+#define TAG_PAYLOAD_LENGTH16       306
+#define TAG_PAYLOAD_LENGTH64       307
+
+#define WS_OP_CONTINUATION_FRAME   0
+#define WS_OP_TEXT_FRAME           1
+#define WS_OP_BINARY_FRAME         2
+#define WS_OP_CONNECTION_CLOSE     8
+#define WS_OP_PING                 9
+#define WS_OP_PONG                 10
+
+static inline BOOL WS_OP_IS_FINAL_FRAGMENT(UInt8 frame)
+{
+	return (frame & 0x80) ? YES : NO;
+}
+
+static inline BOOL WS_PAYLOAD_IS_MASKED(UInt8 frame)
+{
+	return (frame & 0x80) ? YES : NO;
+}
+
+static inline NSUInteger WS_PAYLOAD_LENGTH(UInt8 frame)
+{
+	return frame & 0x7F;
+}
+
+@interface WebSocket (PrivateAPI)
+
+- (void)readRequestBody;
+- (void)sendResponseBody;
+- (void)sendResponseHeaders;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation WebSocket
+{
+	BOOL isRFC6455;
+	BOOL nextFrameMasked;
+	NSUInteger nextOpCode;
+	NSData *maskingKey;
+}
+
++ (BOOL)isWebSocketRequest:(HTTPMessage *)request
+{
+	// Request (Draft 75):
+	// 
+	// GET /demo HTTP/1.1
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// Host: example.com
+	// Origin: http://example.com
+	// WebSocket-Protocol: sample
+	// 
+	// 
+	// Request (Draft 76):
+	//
+	// GET /demo HTTP/1.1
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// Host: example.com
+	// Origin: http://example.com
+	// Sec-WebSocket-Protocol: sample
+	// Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
+	// Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
+	// 
+	// ^n:ds[4U
+	
+	// Look for Upgrade: and Connection: headers.
+	// If we find them, and they have the proper value,
+	// we can safely assume this is a websocket request.
+	
+	NSString *upgradeHeaderValue = [request headerField:@"Upgrade"];
+	NSString *connectionHeaderValue = [request headerField:@"Connection"];
+	
+	BOOL isWebSocket = YES;
+	
+	if (!upgradeHeaderValue || !connectionHeaderValue) {
+		isWebSocket = NO;
+	}
+	else if (![upgradeHeaderValue caseInsensitiveCompare:@"WebSocket"] == NSOrderedSame) {
+		isWebSocket = NO;
+	}
+	else if ([connectionHeaderValue rangeOfString:@"Upgrade" options:NSCaseInsensitiveSearch].location == NSNotFound) {
+		isWebSocket = NO;
+	}
+	
+	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isWebSocket ? @"YES" : @"NO"));
+	
+	return isWebSocket;
+}
+
++ (BOOL)isVersion76Request:(HTTPMessage *)request
+{
+	NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
+	NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
+	
+	BOOL isVersion76;
+	
+	if (!key1 || !key2) {
+		isVersion76 = NO;
+	}
+	else {
+		isVersion76 = YES;
+	}
+	
+	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isVersion76 ? @"YES" : @"NO"));
+	
+	return isVersion76;
+}
+
++ (BOOL)isRFC6455Request:(HTTPMessage *)request
+{
+	NSString *key = [request headerField:@"Sec-WebSocket-Key"];
+	BOOL isRFC6455 = (key != nil);
+
+	HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isRFC6455 ? @"YES" : @"NO"));
+
+	return isRFC6455;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Setup and Teardown
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@synthesize websocketQueue;
+
+- (id)initWithRequest:(HTTPMessage *)aRequest socket:(GCDAsyncSocket *)socket
+{
+	HTTPLogTrace();
+	
+	if (aRequest == nil)
+	{
+		return nil;
+	}
+	
+	if ((self = [super init]))
+	{
+		if (HTTP_LOG_VERBOSE)
+		{
+			NSData *requestHeaders = [aRequest messageData];
+			
+			NSString *temp = [[NSString alloc] initWithData:requestHeaders encoding:NSUTF8StringEncoding];
+			HTTPLogVerbose(@"%@[%p] Request Headers:\n%@", THIS_FILE, self, temp);
+		}
+		
+		websocketQueue = dispatch_queue_create("WebSocket", NULL);
+		request = aRequest;
+		
+		asyncSocket = socket;
+		[asyncSocket setDelegate:self delegateQueue:websocketQueue];
+		
+		isOpen = NO;
+		isVersion76 = [[self class] isVersion76Request:request];
+		isRFC6455 = [[self class] isRFC6455Request:request];
+		
+		term = [[NSData alloc] initWithBytes:"\xFF" length:1];
+	}
+	return self;
+}
+
+- (void)dealloc
+{
+	HTTPLogTrace();
+	
+	#if !OS_OBJECT_USE_OBJC
+	dispatch_release(websocketQueue);
+	#endif
+	
+	[asyncSocket setDelegate:nil delegateQueue:NULL];
+	[asyncSocket disconnect];
+}
+
+- (id)delegate
+{
+	__block id result = nil;
+	
+	dispatch_sync(websocketQueue, ^{
+		result = delegate;
+	});
+	
+	return result;
+}
+
+- (void)setDelegate:(id)newDelegate
+{
+	dispatch_async(websocketQueue, ^{
+		delegate = newDelegate;
+	});
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Start and Stop
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Starting point for the WebSocket after it has been fully initialized (including subclasses).
+ * This method is called by the HTTPConnection it is spawned from.
+**/
+- (void)start
+{
+	// This method is not exactly designed to be overriden.
+	// Subclasses are encouraged to override the didOpen method instead.
+	
+	dispatch_async(websocketQueue, ^{ @autoreleasepool {
+		
+		if (isStarted) return;
+		isStarted = YES;
+		
+		if (isVersion76)
+		{
+			[self readRequestBody];
+		}
+		else
+		{
+			[self sendResponseHeaders];
+			[self didOpen];
+		}
+	}});
+}
+
+/**
+ * This method is called by the HTTPServer if it is asked to stop.
+ * The server, in turn, invokes stop on each WebSocket instance.
+**/
+- (void)stop
+{
+	// This method is not exactly designed to be overriden.
+	// Subclasses are encouraged to override the didClose method instead.
+	
+	dispatch_async(websocketQueue, ^{ @autoreleasepool {
+		
+		[asyncSocket disconnect];
+	}});
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark HTTP Response
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)readRequestBody
+{
+	HTTPLogTrace();
+	
+	NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a request body");
+	
+	[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_HTTP_REQUEST_BODY];
+}
+
+- (NSString *)originResponseHeaderValue
+{
+	HTTPLogTrace();
+	
+	NSString *origin = [request headerField:@"Origin"];
+	
+	if (origin == nil)
+	{
+		NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
+		
+		return [NSString stringWithFormat:@"http://localhost:%@", port];
+	}
+	else
+	{
+		return origin;
+	}
+}
+
+- (NSString *)locationResponseHeaderValue
+{
+	HTTPLogTrace();
+	
+	NSString *location;
+	
+	NSString *scheme = [asyncSocket isSecure] ? @"wss" : @"ws";
+	NSString *host = [request headerField:@"Host"];
+	
+	NSString *requestUri = [[request url] relativeString];
+	
+	if (host == nil)
+	{
+		NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
+		
+		location = [NSString stringWithFormat:@"%@://localhost:%@%@", scheme, port, requestUri];
+	}
+	else
+	{
+		location = [NSString stringWithFormat:@"%@://%@%@", scheme, host, requestUri];
+	}
+	
+	return location;
+}
+
+- (NSString *)secWebSocketKeyResponseHeaderValue {
+	NSString *key = [request headerField: @"Sec-WebSocket-Key"];
+	NSString *guid = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+	return [[key stringByAppendingString: guid] dataUsingEncoding: NSUTF8StringEncoding].sha1Digest.base64Encoded;
+}
+
+- (void)sendResponseHeaders
+{
+	HTTPLogTrace();
+	
+	// Request (Draft 75):
+	// 
+	// GET /demo HTTP/1.1
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// Host: example.com
+	// Origin: http://example.com
+	// WebSocket-Protocol: sample
+	// 
+	// 
+	// Request (Draft 76):
+	//
+	// GET /demo HTTP/1.1
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// Host: example.com
+	// Origin: http://example.com
+	// Sec-WebSocket-Protocol: sample
+	// Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
+	// Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
+	// 
+	// ^n:ds[4U
+
+	
+	// Response (Draft 75):
+	// 
+	// HTTP/1.1 101 Web Socket Protocol Handshake
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// WebSocket-Origin: http://example.com
+	// WebSocket-Location: ws://example.com/demo
+	// WebSocket-Protocol: sample
+	// 
+	// 
+	// Response (Draft 76):
+	//
+	// HTTP/1.1 101 WebSocket Protocol Handshake
+	// Upgrade: WebSocket
+	// Connection: Upgrade
+	// Sec-WebSocket-Origin: http://example.com
+	// Sec-WebSocket-Location: ws://example.com/demo
+	// Sec-WebSocket-Protocol: sample
+	// 
+	// 8jKS'y:G*Co,Wxa-
+
+	
+	HTTPMessage *wsResponse = [[HTTPMessage alloc] initResponseWithStatusCode:101
+	                                                              description:@"Web Socket Protocol Handshake"
+	                                                                  version:HTTPVersion1_1];
+	
+	[wsResponse setHeaderField:@"Upgrade" value:@"WebSocket"];
+	[wsResponse setHeaderField:@"Connection" value:@"Upgrade"];
+	
+	// Note: It appears that WebSocket-Origin and WebSocket-Location
+	// are required for Google's Chrome implementation to work properly.
+	// 
+	// If we don't send either header, Chrome will never report the WebSocket as open.
+	// If we only send one of the two, Chrome will immediately close the WebSocket.
+	// 
+	// In addition to this it appears that Chrome's implementation is very picky of the values of the headers.
+	// They have to match exactly with what Chrome sent us or it will close the WebSocket.
+	
+	NSString *originValue = [self originResponseHeaderValue];
+	NSString *locationValue = [self locationResponseHeaderValue];
+	
+	NSString *originField = isVersion76 ? @"Sec-WebSocket-Origin" : @"WebSocket-Origin";
+	NSString *locationField = isVersion76 ? @"Sec-WebSocket-Location" : @"WebSocket-Location";
+	
+	[wsResponse setHeaderField:originField value:originValue];
+	[wsResponse setHeaderField:locationField value:locationValue];
+	
+	NSString *acceptValue = [self secWebSocketKeyResponseHeaderValue];
+	if (acceptValue) {
+		[wsResponse setHeaderField: @"Sec-WebSocket-Accept" value: acceptValue];
+	}
+
+	NSData *responseHeaders = [wsResponse messageData];
+	
+	
+	if (HTTP_LOG_VERBOSE)
+	{
+		NSString *temp = [[NSString alloc] initWithData:responseHeaders encoding:NSUTF8StringEncoding];
+		HTTPLogVerbose(@"%@[%p] Response Headers:\n%@", THIS_FILE, self, temp);
+	}
+	
+	[asyncSocket writeData:responseHeaders withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_HEADERS];
+}
+
+- (NSData *)processKey:(NSString *)key
+{
+	HTTPLogTrace();
+	
+	unichar c;
+	NSUInteger i;
+	NSUInteger length = [key length];
+	
+	// Concatenate the digits into a string,
+	// and count the number of spaces.
+	
+	NSMutableString *numStr = [NSMutableString stringWithCapacity:10];
+	long long numSpaces = 0;
+	
+	for (i = 0; i < length; i++)
+	{
+		c = [key characterAtIndex:i];
+		
+		if (c >= '0' && c <= '9')
+		{
+			[numStr appendFormat:@"%C", c];
+		}
+		else if (c == ' ')
+		{
+			numSpaces++;
+		}
+	}
+	
+	long long num = strtoll([numStr UTF8String], NULL, 10);
+	
+	long long resultHostNum;
+	
+	if (numSpaces == 0)
+		resultHostNum = 0;
+	else
+		resultHostNum = num / numSpaces;
+	
+	HTTPLogVerbose(@"key(%@) -> %qi / %qi = %qi", key, num, numSpaces, resultHostNum);
+	
+	// Convert result to 4 byte big-endian (network byte order)
+	// and then convert to raw data.
+	
+	UInt32 result = OSSwapHostToBigInt32((uint32_t)resultHostNum);
+	
+	return [NSData dataWithBytes:&result length:4];
+}
+
+- (void)sendResponseBody:(NSData *)d3
+{
+	HTTPLogTrace();
+	
+	NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a response body");
+	NSAssert([d3 length] == 8, @"Invalid requestBody length");
+	
+	NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
+	NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
+	
+	NSData *d1 = [self processKey:key1];
+	NSData *d2 = [self processKey:key2];
+	
+	// Concatenated d1, d2 & d3
+	
+	NSMutableData *d0 = [NSMutableData dataWithCapacity:(4+4+8)];
+	[d0 appendData:d1];
+	[d0 appendData:d2];
+	[d0 appendData:d3];
+	
+	// Hash the data using MD5
+	
+	NSData *responseBody = [d0 md5Digest];
+	
+	[asyncSocket writeData:responseBody withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_BODY];
+	
+	if (HTTP_LOG_VERBOSE)
+	{
+		NSString *s1 = [[NSString alloc] initWithData:d1 encoding:NSASCIIStringEncoding];
+		NSString *s2 = [[NSString alloc] initWithData:d2 encoding:NSASCIIStringEncoding];
+		NSString *s3 = [[NSString alloc] initWithData:d3 encoding:NSASCIIStringEncoding];
+		
+		NSString *s0 = [[NSString alloc] initWithData:d0 encoding:NSASCIIStringEncoding];
+		
+		NSString *sH = [[NSString alloc] initWithData:responseBody encoding:NSASCIIStringEncoding];
+		
+		HTTPLogVerbose(@"key1 result : raw(%@) str(%@)", d1, s1);
+		HTTPLogVerbose(@"key2 result : raw(%@) str(%@)", d2, s2);
+		HTTPLogVerbose(@"key3 passed : raw(%@) str(%@)", d3, s3);
+		HTTPLogVerbose(@"key0 concat : raw(%@) str(%@)", d0, s0);
+		HTTPLogVerbose(@"responseBody: raw(%@) str(%@)", responseBody, sH);
+		
+	}
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Core Functionality
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)didOpen
+{
+	HTTPLogTrace();
+	
+	// Override me to perform any custom actions once the WebSocket has been opened.
+	// This method is invoked on the websocketQueue.
+	// 
+	// Don't forget to invoke [super didOpen] in your method.
+	
+	// Start reading for messages
+	[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:(isRFC6455 ? TAG_PAYLOAD_PREFIX : TAG_PREFIX)];
+	
+	// Notify delegate
+	if ([delegate respondsToSelector:@selector(webSocketDidOpen:)])
+	{
+		[delegate webSocketDidOpen:self];
+	}
+}
+
+- (void)sendMessage:(NSString *)msg
+{	
+	NSData *msgData = [msg dataUsingEncoding:NSUTF8StringEncoding];
+	[self sendData:msgData];
+}
+
+- (void)sendData:(NSData *)msgData
+{
+    HTTPLogTrace();
+    
+    NSMutableData *data = nil;
+	
+	if (isRFC6455)
+	{
+		NSUInteger length = msgData.length;
+		if (length <= 125)
+		{
+			data = [NSMutableData dataWithCapacity:(length + 2)];
+			[data appendBytes: "\x81" length:1];
+			UInt8 len = (UInt8)length;
+			[data appendBytes: &len length:1];
+			[data appendData:msgData];
+		}
+		else if (length <= 0xFFFF)
+		{
+			data = [NSMutableData dataWithCapacity:(length + 4)];
+			[data appendBytes: "\x81\x7E" length:2];
+			UInt16 len = (UInt16)length;
+			[data appendBytes: (UInt8[]){len >> 8, len & 0xFF} length:2];
+			[data appendData:msgData];
+		}
+		else
+		{
+			data = [NSMutableData dataWithCapacity:(length + 10)];
+			[data appendBytes: "\x81\x7F" length:2];
+			[data appendBytes: (UInt8[]){0, 0, 0, 0, (UInt8)(length >> 24), (UInt8)(length >> 16), (UInt8)(length >> 8), length & 0xFF} length:8];
+			[data appendData:msgData];
+		}
+	}
+	else
+	{
+		data = [NSMutableData dataWithCapacity:([msgData length] + 2)];
+        
+		[data appendBytes:"\x00" length:1];
+		[data appendData:msgData];
+		[data appendBytes:"\xFF" length:1];
+	}
+	
+	// Remember: GCDAsyncSocket is thread-safe
+	
+	[asyncSocket writeData:data withTimeout:TIMEOUT_NONE tag:0];
+}
+
+- (void)didReceiveMessage:(NSString *)msg
+{
+	HTTPLogTrace();
+	
+	// Override me to process incoming messages.
+	// This method is invoked on the websocketQueue.
+	// 
+	// For completeness, you should invoke [super didReceiveMessage:msg] in your method.
+	
+	// Notify delegate
+	if ([delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)])
+	{
+		[delegate webSocket:self didReceiveMessage:msg];
+	}
+}
+
+- (void)didClose
+{
+	HTTPLogTrace();
+	
+	// Override me to perform any cleanup when the socket is closed
+	// This method is invoked on the websocketQueue.
+	// 
+	// Don't forget to invoke [super didClose] at the end of your method.
+	
+	// Notify delegate
+	if ([delegate respondsToSelector:@selector(webSocketDidClose:)])
+	{
+		[delegate webSocketDidClose:self];
+	}
+	
+	// Notify HTTPServer
+	[[NSNotificationCenter defaultCenter] postNotificationName:WebSocketDidDieNotification object:self];
+}
+
+#pragma mark WebSocket Frame
+
+- (BOOL)isValidWebSocketFrame:(UInt8)frame
+{
+	NSUInteger rsv =  frame & 0x70;
+	NSUInteger opcode = frame & 0x0F;
+	if (rsv || (3 <= opcode && opcode <= 7) || (0xB <= opcode && opcode <= 0xF))
+	{
+		return NO;
+	}
+	return YES;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark AsyncSocket Delegate
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// 0                   1                   2                   3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-------+-+-------------+-------------------------------+
+// |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+// |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+// |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+// | |1|2|3|       |K|             |                               |
+// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+// |     Extended payload length continued, if payload len == 127  |
+// + - - - - - - - - - - - - - - - +-------------------------------+
+// |                               |Masking-key, if MASK set to 1  |
+// +-------------------------------+-------------------------------+
+// | Masking-key (continued)       |          Payload Data         |
+// +-------------------------------- - - - - - - - - - - - - - - - +
+// :                     Payload Data continued ...                :
+// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+// |                     Payload Data continued ...                |
+// +---------------------------------------------------------------+
+
+- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
+{
+	HTTPLogTrace();
+	
+	if (tag == TAG_HTTP_REQUEST_BODY)
+	{
+		[self sendResponseHeaders];
+		[self sendResponseBody:data];
+		[self didOpen];
+	}
+	else if (tag == TAG_PREFIX)
+	{
+		UInt8 *pFrame = (UInt8 *)[data bytes];
+		UInt8 frame = *pFrame;
+		
+		if (frame <= 0x7F)
+		{
+			[asyncSocket readDataToData:term withTimeout:TIMEOUT_NONE tag:TAG_MSG_PLUS_SUFFIX];
+		}
+		else
+		{
+			// Unsupported frame type
+			[self didClose];
+		}
+	}
+	else if (tag == TAG_PAYLOAD_PREFIX)
+	{
+		UInt8 *pFrame = (UInt8 *)[data bytes];
+		UInt8 frame = *pFrame;
+
+		if ([self isValidWebSocketFrame: frame])
+		{
+			nextOpCode = (frame & 0x0F);
+			[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH];
+		}
+		else
+		{
+			// Unsupported frame type
+			[self didClose];
+		}
+	}
+	else if (tag == TAG_PAYLOAD_LENGTH)
+	{
+		UInt8 frame = *(UInt8 *)[data bytes];
+		BOOL masked = WS_PAYLOAD_IS_MASKED(frame);
+		NSUInteger length = WS_PAYLOAD_LENGTH(frame);
+		nextFrameMasked = masked;
+		maskingKey = nil;
+		if (length <= 125)
+		{
+			if (nextFrameMasked)
+			{
+				[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
+			}
+			[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
+		}
+		else if (length == 126)
+		{
+			[asyncSocket readDataToLength:2 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH16];
+		}
+		else
+		{
+			[asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH64];
+		}
+	}
+	else if (tag == TAG_PAYLOAD_LENGTH16)
+	{
+		UInt8 *pFrame = (UInt8 *)[data bytes];
+		NSUInteger length = ((NSUInteger)pFrame[0] << 8) | (NSUInteger)pFrame[1];
+		if (nextFrameMasked) {
+			[asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
+		}
+		[asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
+	}
+	else if (tag == TAG_PAYLOAD_LENGTH64)
+	{
+		// FIXME: 64bit data size in memory?
+		[self didClose];
+	}
+	else if (tag == TAG_MSG_WITH_LENGTH)
+	{
+		NSUInteger msgLength = [data length];
+		if (nextFrameMasked && maskingKey) {
+			NSMutableData *masked = data.mutableCopy;
+			UInt8 *pData = (UInt8 *)masked.mutableBytes;
+			UInt8 *pMask = (UInt8 *)maskingKey.bytes;
+			for (NSUInteger i = 0; i < msgLength; i++)
+			{
+				pData[i] = pData[i] ^ pMask[i % 4];
+			}
+			data = masked;
+		}
+		if (nextOpCode == WS_OP_TEXT_FRAME)
+		{
+			NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
+			[self didReceiveMessage:msg];
+		}
+		else
+		{
+			[self didClose];
+			return;
+		}
+
+		// Read next frame
+		[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_PREFIX];
+	}
+	else if (tag == TAG_MSG_MASKING_KEY)
+	{
+		maskingKey = data.copy;
+	}
+	else
+	{
+		NSUInteger msgLength = [data length] - 1; // Excluding ending 0xFF frame
+		
+		NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
+		
+		[self didReceiveMessage:msg];
+		
+		
+		// Read next message
+		[asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PREFIX];
+	}
+}
+
+- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error
+{
+	HTTPLogTrace2(@"%@[%p]: socketDidDisconnect:withError: %@", THIS_FILE, self, error);
+	
+	[self didClose];
+}
+
+@end

+ 7 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.h

@@ -0,0 +1,7 @@
+#import "HTTPConnection.h"
+
+@interface DAVConnection : HTTPConnection {
+	id requestContentBody;
+  NSOutputStream* requestContentStream;
+}
+@end

+ 160 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.m

@@ -0,0 +1,160 @@
+#import "DAVConnection.h"
+#import "HTTPMessage.h"
+#import "HTTPFileResponse.h"
+#import "HTTPAsyncFileResponse.h"
+#import "PUTResponse.h"
+#import "DELETEResponse.h"
+#import "DAVResponse.h"
+#import "HTTPLogging.h"
+
+#define HTTP_BODY_MAX_MEMORY_SIZE (1024 * 1024)
+#define HTTP_ASYNC_FILE_RESPONSE_THRESHOLD (16 * 1024 * 1024)
+
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+
+@implementation DAVConnection
+
+- (void) dealloc {
+  [requestContentStream close];
+  
+}
+
+- (BOOL) supportsMethod:(NSString*)method atPath:(NSString*)path {
+  // HTTPFileResponse & HTTPAsyncFileResponse
+  if ([method isEqualToString:@"GET"]) return YES;
+	if ([method isEqualToString:@"HEAD"]) return YES;
+  
+  // PUTResponse
+  if ([method isEqualToString:@"PUT"]) return YES;
+  
+  // DELETEResponse
+  if ([method isEqualToString:@"DELETE"]) return YES;
+	
+  // DAVResponse
+  if ([method isEqualToString:@"OPTIONS"]) return YES;
+  if ([method isEqualToString:@"PROPFIND"]) return YES;
+  if ([method isEqualToString:@"MKCOL"]) return YES;
+  if ([method isEqualToString:@"MOVE"]) return YES;
+  if ([method isEqualToString:@"COPY"]) return YES;
+  if ([method isEqualToString:@"LOCK"]) return YES;
+  if ([method isEqualToString:@"UNLOCK"]) return YES;
+  
+  return NO;
+}
+
+- (BOOL) expectsRequestBodyFromMethod:(NSString*)method atPath:(NSString*)path {
+  // PUTResponse
+  if ([method isEqualToString:@"PUT"]) {
+    return YES;
+	}
+  
+  // DAVResponse
+  if ([method isEqual:@"PROPFIND"] || [method isEqual:@"MKCOL"]) {
+    return [request headerField:@"Content-Length"] ? YES : NO;
+  }
+  if ([method isEqual:@"LOCK"]) {
+    return YES;
+  }
+  
+  return NO;
+}
+
+- (void) prepareForBodyWithSize:(UInt64)contentLength {
+  NSAssert(requestContentStream == nil, @"requestContentStream should be nil");
+  NSAssert(requestContentBody == nil, @"requestContentBody should be nil");
+  
+  if (contentLength > HTTP_BODY_MAX_MEMORY_SIZE) {
+    requestContentBody = [[NSTemporaryDirectory() stringByAppendingString:[[NSProcessInfo processInfo] globallyUniqueString]] copy];
+    requestContentStream = [[NSOutputStream alloc] initToFileAtPath:requestContentBody append:NO];
+    [requestContentStream open];
+  } else {
+    requestContentBody = [[NSMutableData alloc] initWithCapacity:(NSUInteger)contentLength];
+    requestContentStream = nil;
+  }
+}
+
+- (void) processBodyData:(NSData*)postDataChunk {
+	NSAssert(requestContentBody != nil, @"requestContentBody should not be nil");
+  if (requestContentStream) {
+    [requestContentStream write:[postDataChunk bytes] maxLength:[postDataChunk length]];
+  } else {
+    [(NSMutableData*)requestContentBody appendData:postDataChunk];
+  }
+}
+
+- (void) finishBody {
+  NSAssert(requestContentBody != nil, @"requestContentBody should not be nil");
+  if (requestContentStream) {
+    [requestContentStream close];
+    requestContentStream = nil;
+  }
+}
+
+- (void)finishResponse {
+  NSAssert(requestContentStream == nil, @"requestContentStream should be nil");
+  requestContentBody = nil;
+  
+  [super finishResponse];
+}
+
+- (NSObject<HTTPResponse>*) httpResponseForMethod:(NSString*)method URI:(NSString*)path {
+  if ([method isEqualToString:@"HEAD"] || [method isEqualToString:@"GET"]) {
+    NSString* filePath = [self filePathForURI:path allowDirectory:NO];
+    if (filePath) {
+      NSDictionary* fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL];
+      if (fileAttributes) {
+        if ([[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue] > HTTP_ASYNC_FILE_RESPONSE_THRESHOLD) {
+          return [[HTTPAsyncFileResponse alloc] initWithFilePath:filePath forConnection:self];
+        } else {
+          return [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
+        }
+      }
+    }
+  }
+	
+	if ([method isEqualToString:@"PUT"]) {
+    NSString* filePath = [self filePathForURI:path allowDirectory:YES];
+    if (filePath) {
+      if ([requestContentBody isKindOfClass:[NSString class]]) {
+        return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyFile:requestContentBody];
+      } else if ([requestContentBody isKindOfClass:[NSData class]]) {
+        return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyData:requestContentBody];
+      } else {
+        HTTPLogError(@"Internal error");
+      }
+    }
+  }
+	
+	if ([method isEqualToString:@"DELETE"]) {
+    NSString* filePath = [self filePathForURI:path allowDirectory:YES];
+    if (filePath) {
+      return [[DELETEResponse alloc] initWithFilePath:filePath];
+    }
+  }
+  
+  if ([method isEqualToString:@"OPTIONS"] || [method isEqualToString:@"PROPFIND"] || [method isEqualToString:@"MKCOL"] ||
+    [method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"] || [method isEqualToString:@"LOCK"] || [method isEqualToString:@"UNLOCK"]) {
+    NSString* filePath = [self filePathForURI:path allowDirectory:YES];
+    if (filePath) {
+      NSString* rootPath = [config documentRoot];
+      NSString* resourcePath = [filePath substringFromIndex:([rootPath length] + 1)];
+      if (requestContentBody) {
+        if ([requestContentBody isKindOfClass:[NSString class]]) {
+          requestContentBody = [NSData dataWithContentsOfFile:requestContentBody];
+        } else if (![requestContentBody isKindOfClass:[NSData class]]) {
+          HTTPLogError(@"Internal error");
+          return nil;
+        }
+      }
+      return [[DAVResponse alloc] initWithMethod:method
+                                          headers:[request allHeaderFields]
+                                         bodyData:requestContentBody
+                                     resourcePath:resourcePath
+                                         rootPath:rootPath];
+    }
+  }
+  
+  return nil;
+}
+
+@end

+ 11 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.h

@@ -0,0 +1,11 @@
+#import "HTTPResponse.h"
+
+@interface DAVResponse : NSObject <HTTPResponse> {
+@private
+  UInt64 _offset;
+  NSMutableDictionary* _headers;
+  NSData* _data;
+  NSInteger _status;
+}
+- (id) initWithMethod:(NSString*)method headers:(NSDictionary*)headers bodyData:(NSData*)body resourcePath:(NSString*)resourcePath rootPath:(NSString*)rootPath;
+@end

+ 372 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.m

@@ -0,0 +1,372 @@
+#import <libxml/parser.h>
+
+#import "DAVResponse.h"
+#import "HTTPLogging.h"
+
+// WebDAV specifications: http://webdav.org/specs/rfc4918.html
+
+typedef enum {
+  kDAVProperty_ResourceType = (1 << 0),
+  kDAVProperty_CreationDate = (1 << 1),
+  kDAVProperty_LastModified = (1 << 2),
+  kDAVProperty_ContentLength = (1 << 3),
+  kDAVAllProperties = kDAVProperty_ResourceType | kDAVProperty_CreationDate | kDAVProperty_LastModified | kDAVProperty_ContentLength
+} DAVProperties;
+
+#define kXMLParseOptions (XML_PARSE_NONET | XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_COMPACT | XML_PARSE_NOWARNING | XML_PARSE_NOERROR)
+
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+
+@implementation DAVResponse
+
+static void _AddPropertyResponse(NSString* itemPath, NSString* resourcePath, DAVProperties properties, NSMutableString* xmlString) {
+  CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)resourcePath, NULL,
+                                                                    CFSTR("<&>?+"), kCFStringEncodingUTF8);
+  if (escapedPath) {
+    NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
+    BOOL isDirectory = [[attributes fileType] isEqualToString:NSFileTypeDirectory];
+    [xmlString appendString:@"<D:response>"];
+      [xmlString appendFormat:@"<D:href>%@</D:href>", escapedPath];
+      [xmlString appendString:@"<D:propstat>"];
+        [xmlString appendString:@"<D:prop>"];
+        
+          if (properties & kDAVProperty_ResourceType) {
+            if (isDirectory) {
+              [xmlString appendString:@"<D:resourcetype><D:collection/></D:resourcetype>"];
+            } else {
+              [xmlString appendString:@"<D:resourcetype/>"];
+            }
+          }
+          
+          if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) {
+            NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
+            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
+            formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
+            formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
+            [xmlString appendFormat:@"<D:creationdate>%@</D:creationdate>", [formatter stringFromDate:[attributes fileCreationDate]]];
+          }
+          
+          if ((properties & kDAVProperty_LastModified) && [attributes objectForKey:NSFileModificationDate]) {
+            NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
+            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
+            formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
+            formatter.dateFormat = @"EEE', 'd' 'MMM' 'yyyy' 'HH:mm:ss' GMT'";
+            [xmlString appendFormat:@"<D:getlastmodified>%@</D:getlastmodified>", [formatter stringFromDate:[attributes fileModificationDate]]];
+          }
+          
+          if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) {
+            [xmlString appendFormat:@"<D:getcontentlength>%qu</D:getcontentlength>", [attributes fileSize]];
+          }
+        
+        [xmlString appendString:@"</D:prop>"];
+        [xmlString appendString:@"<D:status>HTTP/1.1 200 OK</D:status>"];
+      [xmlString appendString:@"</D:propstat>"];
+    [xmlString appendString:@"</D:response>\n"];
+    CFRelease(escapedPath);
+  }
+}
+
+static xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name) {
+  while (child) {
+    if ((child->type == XML_ELEMENT_NODE) && !xmlStrcmp(child->name, name)) {
+      return child;
+    }
+    child = child->next;
+  }
+  return NULL;
+}
+
+- (id) initWithMethod:(NSString*)method headers:(NSDictionary*)headers bodyData:(NSData*)body resourcePath:(NSString*)resourcePath rootPath:(NSString*)rootPath {
+  if ((self = [super init])) {
+    _status = 200;
+    _headers = [[NSMutableDictionary alloc] init];
+    
+    // 10.1 DAV Header
+    if ([method isEqualToString:@"OPTIONS"]) {
+      if ([[headers objectForKey:@"User-Agent"] hasPrefix:@"WebDAVFS/"]) {  // Mac OS X WebDAV support
+        [_headers setObject:@"1, 2" forKey:@"DAV"];
+      } else {
+        [_headers setObject:@"1" forKey:@"DAV"];
+      }
+    }
+    
+    // 9.1 PROPFIND Method
+    if ([method isEqualToString:@"PROPFIND"]) {
+      NSInteger depth;
+      NSString* depthHeader = [headers objectForKey:@"Depth"];
+      if ([depthHeader isEqualToString:@"0"]) {
+        depth = 0;
+      } else if ([depthHeader isEqualToString:@"1"]) {
+        depth = 1;
+      } else {
+        HTTPLogError(@"Unsupported DAV depth \"%@\"", depthHeader);
+        return nil;
+      }
+      
+      DAVProperties properties = 0;
+      xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
+      if (document) {
+        xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"propfind");
+        if (node) {
+          node = _XMLChildWithName(node->children, (const xmlChar*)"prop");
+        }
+        if (node) {
+          node = node->children;
+          while (node) {
+            if (!xmlStrcmp(node->name, (const xmlChar*)"resourcetype")) {
+              properties |= kDAVProperty_ResourceType;
+            } else if (!xmlStrcmp(node->name, (const xmlChar*)"creationdate")) {
+              properties |= kDAVProperty_CreationDate;
+            } else if (!xmlStrcmp(node->name, (const xmlChar*)"getlastmodified")) {
+              properties |= kDAVProperty_LastModified;
+            } else if (!xmlStrcmp(node->name, (const xmlChar*)"getcontentlength")) {
+              properties |= kDAVProperty_ContentLength;
+            } else {
+              HTTPLogWarn(@"Unknown DAV property requested \"%s\"", node->name);
+            }
+            node = node->next;
+          }
+        } else {
+          HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
+        }
+        xmlFreeDoc(document);
+      }
+      if (!properties) {
+        properties = kDAVAllProperties;
+      }
+      
+      NSString* basePath = [rootPath stringByAppendingPathComponent:resourcePath];
+      if (![basePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:basePath]) {
+        return nil;
+      }
+      
+      NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
+      [xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
+      if (![resourcePath hasPrefix:@"/"]) {
+        resourcePath = [@"/" stringByAppendingString:resourcePath];
+      }
+      _AddPropertyResponse(basePath, resourcePath, properties, xmlString);
+      if (depth == 1) {
+        if (![resourcePath hasSuffix:@"/"]) {
+          resourcePath = [resourcePath stringByAppendingString:@"/"];
+        }
+        NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:basePath];
+        NSString* path;
+        while ((path = [enumerator nextObject])) {
+          _AddPropertyResponse([basePath stringByAppendingPathComponent:path], [resourcePath stringByAppendingString:path], properties, xmlString);
+          [enumerator skipDescendents];
+        }
+      }
+      [xmlString appendString:@"</D:multistatus>"];
+      
+      [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
+      _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
+      _status = 207;
+    }
+    
+    // 9.3 MKCOL Method
+    if ([method isEqualToString:@"MKCOL"]) {
+      NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
+      if (![path hasPrefix:rootPath]) {
+        return nil;
+      }
+      
+      if (![[NSFileManager defaultManager] fileExistsAtPath:[path stringByDeletingLastPathComponent]]) {
+        HTTPLogError(@"Missing intermediate collection(s) at \"%@\"", path);
+        _status = 409;
+      } else if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:NULL]) {
+        HTTPLogError(@"Failed creating collection at \"%@\"", path);
+        _status = 405;
+      }
+    }
+    
+    // 9.8 COPY Method
+    // 9.9 MOVE Method
+    if ([method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"]) {
+      if ([method isEqualToString:@"COPY"] && ![[headers objectForKey:@"Depth"] isEqualToString:@"infinity"]) {
+        HTTPLogError(@"Unsupported DAV depth \"%@\"", [headers objectForKey:@"Depth"]);
+        return nil;
+      }
+      
+      NSString* sourcePath = [rootPath stringByAppendingPathComponent:resourcePath];
+      if (![sourcePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) {
+        return nil;
+      }
+      
+      NSString* destination = [headers objectForKey:@"Destination"];
+      NSRange range = [destination rangeOfString:[headers objectForKey:@"Host"]];
+      if (range.location == NSNotFound) {
+        return nil;
+      }
+      NSString* destinationPath = [rootPath stringByAppendingPathComponent:
+        [[destination substringFromIndex:(range.location + range.length)] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+      if (![destinationPath hasPrefix:rootPath] || [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
+        return nil;
+      }
+      
+      BOOL isDirectory;
+      if (![[NSFileManager defaultManager] fileExistsAtPath:[destinationPath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
+        HTTPLogError(@"Invalid destination path \"%@\"", destinationPath);
+        _status = 409;
+      } else {
+        BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:destinationPath];
+        if (existing && [[headers objectForKey:@"Overwrite"] isEqualToString:@"F"]) {
+          HTTPLogError(@"Pre-existing destination path \"%@\"", destinationPath);
+          _status = 412;
+        } else {
+          if ([method isEqualToString:@"COPY"]) {
+            if ([[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
+              _status = existing ? 204 : 201;
+            } else {
+              HTTPLogError(@"Failed copying \"%@\" to \"%@\"", sourcePath, destinationPath);
+              _status = 403;
+            }
+          } else {
+            if ([[NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
+              _status = existing ? 204 : 201;
+            } else {
+              HTTPLogError(@"Failed moving \"%@\" to \"%@\"", sourcePath, destinationPath);
+              _status = 403;
+            }
+          }
+        }
+      }
+    }
+    
+    // 9.10 LOCK Method - TODO: Actually lock the resource
+    if ([method isEqualToString:@"LOCK"]) {
+      NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
+      if (![path hasPrefix:rootPath]) {
+        return nil;
+      }
+      
+      NSString* depth = [headers objectForKey:@"Depth"];
+      NSString* scope = nil;
+      NSString* type = nil;
+      NSString* owner = nil;
+      NSString* token = nil;
+      xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
+      if (document) {
+        xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"lockinfo");
+        if (node) {
+          xmlNodePtr scopeNode = _XMLChildWithName(node->children, (const xmlChar*)"lockscope");
+          if (scopeNode && scopeNode->children && scopeNode->children->name) {
+            scope = [NSString stringWithUTF8String:(const char*)scopeNode->children->name];
+          }
+          xmlNodePtr typeNode = _XMLChildWithName(node->children, (const xmlChar*)"locktype");
+          if (typeNode && typeNode->children && typeNode->children->name) {
+            type = [NSString stringWithUTF8String:(const char*)typeNode->children->name];
+          }
+          xmlNodePtr ownerNode = _XMLChildWithName(node->children, (const xmlChar*)"owner");
+          if (ownerNode) {
+            ownerNode = _XMLChildWithName(ownerNode->children, (const xmlChar*)"href");
+            if (ownerNode && ownerNode->children && ownerNode->children->content) {
+              owner = [NSString stringWithUTF8String:(const char*)ownerNode->children->content];
+            }
+          }
+        } else {
+          HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
+        }
+        xmlFreeDoc(document);
+      } else {
+		  // No body, see if they're trying to refresh an existing lock.  If so, then just fake up the scope, type and depth so we fall
+		  // into the lock create case.
+		  NSString* lockToken;
+		  if ((lockToken = [headers objectForKey:@"If"]) != nil) {
+			  scope = @"exclusive";
+			  type = @"write";
+			  depth = @"0";
+			  token = [lockToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"(<>)"]];
+		  }
+	  }
+      if ([scope isEqualToString:@"exclusive"] && [type isEqualToString:@"write"] && [depth isEqualToString:@"0"] &&
+        ([[NSFileManager defaultManager] fileExistsAtPath:path] || [[NSData data] writeToFile:path atomically:YES])) {
+        NSString* timeout = [headers objectForKey:@"Timeout"];
+		if (!token) {
+          CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+          NSString *uuidStr = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
+          token = [NSString stringWithFormat:@"urn:uuid:%@", uuidStr];
+          CFRelease(uuid);
+		}
+        
+        NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
+        [xmlString appendString:@"<D:prop xmlns:D=\"DAV:\">\n"];
+        [xmlString appendString:@"<D:lockdiscovery>\n<D:activelock>\n"];
+        [xmlString appendFormat:@"<D:locktype><D:%@/></D:locktype>\n", type];
+        [xmlString appendFormat:@"<D:lockscope><D:%@/></D:lockscope>\n", scope];
+        [xmlString appendFormat:@"<D:depth>%@</D:depth>\n", depth];
+        if (owner) {
+          [xmlString appendFormat:@"<D:owner><D:href>%@</D:href></D:owner>\n", owner];
+        }
+        if (timeout) {
+          [xmlString appendFormat:@"<D:timeout>%@</D:timeout>\n", timeout];
+        }
+        [xmlString appendFormat:@"<D:locktoken><D:href>%@</D:href></D:locktoken>\n", token];
+		NSString* lockroot = [@"http://" stringByAppendingString:[[headers objectForKey:@"Host"] stringByAppendingString:[@"/" stringByAppendingString:resourcePath]]];
+        [xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
+        [xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
+        [xmlString appendString:@"</D:prop>"];
+        
+        [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
+        _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
+        _status = 200;
+        HTTPLogVerbose(@"Pretending to lock \"%@\"", resourcePath);
+      } else {
+        HTTPLogError(@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depth, resourcePath);
+        _status = 403;
+      }
+    }
+    
+    // 9.11 UNLOCK Method - TODO: Actually unlock the resource
+    if ([method isEqualToString:@"UNLOCK"]) {
+      NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
+      if (![path hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
+        return nil;
+      }
+      
+      NSString* token = [headers objectForKey:@"Lock-Token"];
+      _status = token ? 204 : 400;
+      HTTPLogVerbose(@"Pretending to unlock \"%@\"", resourcePath);
+    }
+    
+  }
+  return self;
+}
+
+
+- (UInt64) contentLength {
+  return _data ? _data.length : 0;
+}
+
+- (UInt64) offset {
+  return _offset;
+}
+
+- (void) setOffset:(UInt64)offset {
+  _offset = offset;
+}
+
+- (NSData*) readDataOfLength:(NSUInteger)lengthParameter {
+  if (_data) {
+    NSUInteger remaining = _data.length - (NSUInteger)_offset;
+    NSUInteger length = lengthParameter < remaining ? lengthParameter : remaining;
+    void* bytes = (void*)(_data.bytes + _offset);
+    _offset += length;
+    return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO];
+  }
+  return nil;
+}
+
+- (BOOL) isDone {
+  return _data ? _offset == _data.length : YES;
+}
+
+- (NSInteger) status {
+  return _status;
+}
+
+- (NSDictionary*) httpHeaders {
+  return _headers;
+}
+
+@end

+ 7 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.h

@@ -0,0 +1,7 @@
+#import "HTTPResponse.h"
+
+@interface DELETEResponse : NSObject <HTTPResponse> {
+  NSInteger _status;
+}
+- (id) initWithFilePath:(NSString*)path;
+@end

+ 49 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.m

@@ -0,0 +1,49 @@
+#import "DELETEResponse.h"
+#import "HTTPLogging.h"
+
+// HTTP methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+// HTTP headers: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+// HTTP status codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+
+@implementation DELETEResponse
+
+- (id) initWithFilePath:(NSString*)path {
+  if ((self = [super init])) {
+    BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
+    if ([[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
+      _status = exists ? 200 : 204;
+    } else {
+      HTTPLogError(@"Failed deleting \"%@\"", path);
+      _status = 404;
+    }
+  }
+  return self;
+}
+
+- (UInt64) contentLength {
+  return 0;
+}
+
+- (UInt64) offset {
+  return 0;
+}
+
+- (void)setOffset:(UInt64)offset {
+  ;
+}
+
+- (NSData*) readDataOfLength:(NSUInteger)length {
+  return nil;
+}
+
+- (BOOL) isDone {
+  return YES;
+}
+
+- (NSInteger) status {
+  return _status;
+}
+
+@end

+ 8 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.h

@@ -0,0 +1,8 @@
+#import "HTTPResponse.h"
+
+@interface PUTResponse : NSObject <HTTPResponse> {
+  NSInteger _status;
+}
+- (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyData:(NSData*)body;
+- (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyFile:(NSString*)body;
+@end

+ 69 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.m

@@ -0,0 +1,69 @@
+#import "PUTResponse.h"
+#import "HTTPLogging.h"
+
+// HTTP methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+// HTTP headers: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+// HTTP status codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+
+static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
+
+@implementation PUTResponse
+
+- (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers body:(id)body {
+  if ((self = [super init])) {
+    if ([headers objectForKey:@"Content-Range"]) {
+      HTTPLogError(@"Content-Range not supported for upload to \"%@\"", path);
+      _status = 400;
+    } else {
+      BOOL overwrite = [[NSFileManager defaultManager] fileExistsAtPath:path];
+      BOOL success;
+      if ([body isKindOfClass:[NSString class]]) {
+        [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
+        success = [[NSFileManager defaultManager] moveItemAtPath:body toPath:path error:NULL];
+      } else {
+        success = [body writeToFile:path atomically:YES];
+      }
+      if (success) {
+        _status = overwrite ? 200 : 201;
+      } else {
+        HTTPLogError(@"Failed writing upload to \"%@\"", path);
+        _status = 403;
+      }
+    }
+  }
+  return self;
+}
+
+- (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyData:(NSData*)body {
+  return [self initWithFilePath:path headers:headers body:body];
+}
+
+- (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyFile:(NSString*)body {
+  return [self initWithFilePath:path headers:headers body:body];
+}
+
+- (UInt64) contentLength {
+  return 0;
+}
+
+- (UInt64) offset {
+  return 0;
+}
+
+- (void) setOffset:(UInt64)offset {
+  ;
+}
+
+- (NSData*) readDataOfLength:(NSUInteger)length {
+  return nil;
+}
+
+- (BOOL) isDone {
+  return YES;
+}
+
+- (NSInteger) status {
+  return _status;
+}
+
+@end

File diff suppressed because it is too large
+ 18 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/LICENSE.txt


+ 27 - 0
KulexiuForTeacher/Pods/CocoaHTTPServer/README.markdown

@@ -0,0 +1,27 @@
+# CocoaHTTPServer
+
+[![Build Status](https://travis-ci.org/robbiehanson/CocoaHTTPServer.svg)](https://travis-ci.org/robbiehanson/CocoaHTTPServer)
+ [![Version](http://img.shields.io/cocoapods/v/CocoaHTTPServer.svg?style=flat)](http://cocoapods.org/?q=CocoaHTTPServer)
+ [![Platform](http://img.shields.io/cocoapods/p/CocoaHTTPServer.svg?style=flat)]()
+ [![License](http://img.shields.io/cocoapods/l/CocoaHTTPServer.svg?style=flat)](https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE)
+
+CocoaHTTPServer is a small, lightweight, embeddable HTTP server for Mac OS X or iOS applications.
+
+Sometimes developers need an embedded HTTP server in their app. Perhaps it's a server application with remote monitoring. Or perhaps it's a desktop application using HTTP for the communication backend. Or perhaps it's an iOS app providing over-the-air access to documents. Whatever your reason, CocoaHTTPServer can get the job done. It provides:
+
+-   Built in support for bonjour broadcasting
+-   IPv4 and IPv6 support
+-   Asynchronous networking using GCD and standard sockets
+-   Password protection support
+-   SSL/TLS encryption support
+-   Extremely FAST and memory efficient
+-   Extremely scalable (built entirely upon GCD)
+-   Heavily commented code
+-   Very easily extensible
+-   WebDAV is supported too!
+
+<br/>
+Can't find the answer to your question in any of the [wiki](https://github.com/robbiehanson/CocoaHTTPServer/wiki) articles? Try the **[mailing list](http://groups.google.com/group/cocoahttpserver)**.
+<br/>
+<br/>
+Love the project? Wanna buy me a coffee? (or a beer :D) [![donation](http://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BHF2DJRETGV5S)

+ 638 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/CHANGELOG.md

@@ -0,0 +1,638 @@
+# [3.8.5 - Xcode 15.3 on Mar 8th, 2024](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tags/3.8.5)
+
+### Public
+
+- Fix build failure due to privacy manifest when using static linking with CocoaPods (#1408)
+- Allow custom mapping of `DDLogFlag` to `os_log_type_t`, fix default mapping for `DDLogFlagWarn` (#1410)
+
+
+# [3.8.4 - Xcode 15.2 on Feb 8th, 2024](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tags/3.8.4)
+
+### Public
+
+- Extend privacy manifest to fulfill validation criteria (#1403)
+- Fix crash when fetching registered classes (#1406)
+
+
+# [3.8.3 - Xcode 15.2 on Feb 5th, 2024](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tags/3.8.3)
+
+### Public
+
+- Add privacy manifest (#1403)
+- Update doc from DDLogLevelWarn to DDLogLevelWarning to match library (#1383)
+- Only cleanup files on configuration change if the manager is used by a file logger (#1398)
+- Fix #1386 again by adding a missing return and adjusting the preprocessor conditionals
+- Fix some C++ warnings
+
+### Internal
+
+- Update copyright for 2024 (#1400)
+- Improve asserts (#1385)
+
+
+# [3.8.2 - Xcode 15.0 on Oct 31st, 2023](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.8.2)
+
+### Public
+
+- App background-mode not correctly detected in app extensions (#1359)
+- Fix DDFileLogger rollingFrequency and maximumFileSize not being honored (#1361)
+- Fix potential crashes when using the new `DDLogMessageFormat` with messages that contain '%'
+- Fix simulator issues when using dynamic registered logging on iOS 17 (#1386)
+- Allow `DDFileLogger` to write in different file formats (#1380)
+
+### Internal
+
+- Generate Podspec (#1360)
+
+
+# [3.8.1 - Xcode 14.3 on Aug 21st, 2023](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.8.1)
+
+### Public
+
+- Silence double conversion warnings for 32-bit watchOS (#1320)
+- Enable ALLOW_TARGET_PLATFORM_SPECIALIZATION (#1321)
+- Update to swift-log 1.5.2, implement metadata providers (#1329)
+- Preserve `messageFormat` in `DDLogMessage` (#1347)
+
+### Internal
+
+- Update copyright for 2023
+
+
+## [3.8.0 - Xcode 14.1 on Nov 2nd, 2022](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.8.0)
+
+### Public
+
+- Add support for Xcode 14 / Swift 5.7 - drop support for Swift < 5.5, iOS/tvOS < 11, macOS < 10.13, watchOS < 4 (#1316)
+- Update README about swift-log usage (#1275)
+- Use dispatch_walltime for scheduling log file rolling timer (#1309)
+
+### Internal
+
+- Add consistent newline to file endings (#1272)
+- Fix error checking in DDFileLogger (#1274)
+- Avoid using NSString format (#1280)
+- Prevent logging to symlink files (#1314)
+
+
+## [3.7.4 - Xcode 13.2 on Dec 16, 2021](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.7.4)
+
+### Public
+
+- Fix swift-tools-version in Package@swift-5.3.swift
+
+
+## [3.7.3 - Xcode 13.2 on Dec 16, 2021](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.7.3)
+
+### Public
+
+- Fix "DDFileLogger: Failed to get offset" when setting maximumFileSize (#1234)
+- Follow-up to add annotations to DDOSLogger (#1248)
+- Fixed nullability conflict in DDDispatchQueueLogFormatter.h (#1252)
+- Add Swift 5.5 support, fix archive build on Xcode 13 (#1253)
+- Fix file access issue in Catalyst apps (#1257)
+- Fix excluded archs in debug build when not mac catalyst (#1260)
+- Bump Xcode last upgraded version to 13.2 (#1265)
+- Don't log warnings for CLI apps in DDTTYLogger (#1269)
+
+
+## [3.7.2 - Xcode 12.4 on Apr 9th, 2021](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.7.2)
+
+### Public
+
+- Re-introduce (and deprecate) `_tag` field to fix breakage in 3.7.1 (#1224)
+
+
+## [3.7.1 - Xcode 12.4 on Apr 7th, 2021](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.7.1)
+
+### Public
+
+- Deprecate `tag` property of `DDLogMessage`, use `representedObject` instead. (#1177, #532)
+- Add per-message synchronous logging control for messages logged via SwiftLog using `DDLogHandler` (#1209)
+- Add TargetConditionals import for Xcode 12.5 (#1210)
+- Prevent logging an error when archiving an already deleted file (#1212)
+- Use inclusive words - denylist / allowlist (#1218)
+- Add `DDAssertionFailure` macro for Objective-C (#1220)
+
+### Internal
+
+- Use setter to replace kvo for `NSFileLogger` (#1180)
+- Use new API for `NSFileHandle` on supported platforms (#1181)
+- Remove unnecessary checks in `DDFileLogger` (#1182)
+- Add an assertion to avoid potential deadlock issues for `flushLog` (#1183)
+
+
+## [3.7.0 - Xcode 12 on Oct 2nd, 2020](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.7.0)
+
+### Public
+
+- **Breaking change**: Dropped support for iOS 8 (#1153)
+- Update SPM tools-version to 5.3 to enable Swift 5.3 support (#1148)
+- Add backend for swift-log (#1164)
+- Specify CocoaPods version to ensure `swift_version` attribute works (#1167)
+- Simplify `DDLogFileManager` callbacks for archived log files (#1166)
+
+
+## [3.6.2 - Xcode 11.6 on July 31st, 2020](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.6.2)
+
+### Public
+- Fix warnings when building with SPM bundled with Swift 5.2 / Xcode 11.4 (#1132)
+- Added Swift name for DDQualityOfServiceName constants.
+- Don't localize timestamps in `DDefaultFileLogFormatter` (#1151)
+- Allow logging arbitrary objects via Swift log functions (#1146)
+
+### Repository
+- Switch from Travis to GitHub Actions (#1135, #1140, #1150, #1152)
+
+
+## [3.6.1 - Xcode 11.3.1 on Jan 25th, 2020](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.6.1)
+
+### Public
+- Improve error handling during log file creation in DDFileLogger & DDLogFileManager (#1103 / #1111)
+- Improve nullability annotations in public headers (#1111 / #1112 / #1119)
+- Added support for thread QOS in DDLogMessage class (#1124)
+
+### Internal
+- Fix rolling timer being rescheduled rapidly due to leeway (#1106 / #1107)
+- Fix -didArchiveLogFile: returning the file name instead of the file path (#1078)
+- Fix setxattr() function usage (#1118)
+- Fix NSDateFormatter thread safety (#1121)
+- Fix -lt_dataForMessage: duplicated code (#1122)
+
+
+## [3.6.0 - Xcode 11 on October 2nd, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.6.0)
+
+### Public
+- Swift Package Manager Support (#1083)
+- New `willLogMessage:` and `didLogMessage:` methods on `DDFileLogger` which provide access to the current log file info.
+
+### Internal
+- Fix issue with log archiving in the simulator (#1098)
+- Limit assertion to non-simulator build (#1100)
+
+
+## [3.5.3 - Xcode 10.2 on Apr 24th, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.5.3)
+
+### Public
+- Additional compatibility with Swift 5 (backwards compatible with Swift 4) (#1043)
+- Fix warning building with Xcode 10.2 (#1059)
+- Set Xcode 10.2 and Swift 5.0 as a default (#1064)
+- Fix format string crash (#1066)
+
+### Internal
+- Fix warning about syntax (#1054) (#1065)
+- Remove banned APIs (#1056) (#1057)
+- Add CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER & fix warnings (#1059)
+- Use LLONG_MAX instead of LONG_LONG_MAX (#1062)
+
+
+## [3.5.2 - Xcode 10.1 on Mar 15th, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.5.2)
+
+### Public
+- Fix reusing of log files after rolling (#1042)
+- Fix creation of too many log files (#1049)
+- Preliminary compatibility with Swift 5 (backwards compatible with Swift 4) (#1044)
+- core: loggers os logger variations have been added (#1039)
+
+### Internal
+- Sync internal queues to prevent cleaning up log files too soon in tests (#1053)
+- DDLog checks for NULL values and for global queue dispatching has been added (#1045)
+
+
+## [3.5.1 - Xcode 10 on Feb 4th, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.5.1)
+
+### Public
+- Fix high CPU usage because of empty fileAttributes and / or too high rollingFrequceny (#1028)
+
+
+## [3.5.0 - Xcode 10 on Jan 25th, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.5.0)
+
+### Public
+- Added `logFileHeader` property to `DDLogFileManagerDefault`. Override to set header for each created file. #998
+- `DDFileLogger` now accepts a `dispatch_queue_t` which it uses to run callbacks. If not provided, the default global queue is used. #1003
+- Added opt-in buffering to `DDFileLogger`. Call `wrapWithBuffer` to create a file logger which buffers. #1001, #1012
+- Add `DDAssert` and `DDAssertionFailure` functions for Swift #934
+- Add `DD_LOG_LEVEL` define (which can be set in `GCC_PREPROCESSOR_DEFINITIONS`) for Swift to set default log level (enables stripping for strings that are not logged). #952
+- Add `asyncLoggingEnabled` global variable to control asynchronous logging. #1019
+
+### Internal
+- Prevent memory access errors caused by a failed fetch #944
+- Fix common warnings emitted by `-Wall`, `-Wconversion`, `-Wextra`, etc #943, #931
+- Fixes issue that could cause log messages to become interleaved when there are multiple `DDFileLogger`s #985
+- `DispatchQueueFormatter` knows about `com.apple.root.default-qos.overcommit` now #932
+- Fix thread safety issues in `DDFileLogger`. Makes it a little harder to deadlock in some cases. #986, #1003, #946
+- Fix availability checks and memory leak #996
+
+### Repository
+- Reduce podspec to two subspecs and remove customized modulemap #976
+- Add danger support for PR checks #962 - fixes #956
+- Merged framework targets + using `xcconfig` + deployment target `iOS 8` and `Mac OS 10.10` #959 e97da34
+- Documentation update #955 e7414ae 0239196 #933
+- Full links to Docs and other resources so they are resolved on external pages (i.e. https://cocoapods.org/pods/CocoaLumberjack) e9d6971
+- Replace `OSAtomic` with `stdatomic` in `DDDispatchQueueLogFormatter` #957 #958
+- Add Stale Bot + configuration #953
+- Update to Xcode 10 and Swift 4.2 compiler #950
+- Xcode 10 scheme changes #949
+- Update incomplete BSD 3-Clause License #942
+- Updated to CocoaPods 1.5.3 2d0590f
+- Use Xcode 9.4 image for tests #939
+- Xcode (schemes) version bumps #938
+- Update demo and documentation about CustomLogLevels #1023
+
+
+## [3.4.2 - Xcode 9.3 on Apr 17th, 2018](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.4.2)
+- Update README.md #912
+- Fixed typo in pull request template #913
+- Fix `-Wimplicit-retain-self` warnings #915
+- Update memory management in dynamic logging #916
+- Xcode 9.3 support #921 #923 #926 #927
+- Add extern "C" for Objective-C++ #922
+- Add `flush` method to the `DDFileLogger` #928
+
+
+## [3.4.1 - Xcode 9.1 on Jan 26th, 2018](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.4.1)
+- Fix `DDLogFileManagerDefault` `-isLogFile` #909
+- Fix locking the main thread #911
+
+
+## [3.4.0 - Xcode 9.1 on Jan 3rd, 2018](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.4.0)
+- Fix Travis CI #895
+- Fix typos #896
+- Fix schemes and link errors #897 #899 #903 #907
+
+
+## [3.3.0 - Xcode 9 on Oct 3rd, 2017](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.3.0)
+- Xcode 9 support and Swift 4 support #890 #893
+- Replace `OSSpinLock` with `pthread_mutex` #889
+
+
+## [3.2.1 - Xcode 9 beta on Aug 21st, 2017](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.2.1)
+- Xcode 9 beta support #874 #873 #884 #883 #882
+- Fixed some issues around deleting log files #868 #879
+- update 'Use Log Level per Logger' doc #888
+- Remove empty asset catalogs so that they don't show up in Open Quickly #877
+- Fixed typo in pull request template #880
+
+
+## [3.2.0 - Swift 3.0.0, Xcode 8.3 on May 3rd, 2017](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.2.0)
+- Xcode 8.3 support #860 #853
+- added a very basic `os_log` (unified logging) logger #850 #856
+- Use `NSFileProtectionType` instead of `NSString` #866
+- Optimized timestamp calculation in `DDTTYLogger` #851
+- Updated docs #864
+- Fix Travis #863
+- Fixed nullability of `DDLogMessage.function` #849 `DDExtractFileNameWithoutExtension` #845
+- Ignore `Carthage/Build` directory #862
+- Updated schemes #859 #857
+
+
+## [3.1.0 - Swift 3.0.1, Xcode 8.1 on Feb 22nd, 2017](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.1.0)
+- Swift 3.0.1 and Xcode 8.1 support via #816
+- Fix Carthage build and updated the podspec structure #819 #818 #784 #790 #782 #778 #815
+- Fix CLIColor.h not included in umbrella header #781 #796 #813 #783
+- Fix crash in `[DDLog log:level:flag:context:file:function:line:tag:format:]` #831 #830
+- Code improvements:
+  - using class properties #779
+  - nullability #803 #809 #776
+  - fix static analyzer issues #822 #828
+  - optimized `USE_DISPATCH_CURRENT_QUEUE_LABEL` and `USE_DISPATCH_GET_CURRENT_QUEUE` macros #829
+  - fixed dispatch_source_set_timer() usage #834
+  - fixed misuse of non null parameter in `DDFileLogger fileAttributes` #835
+  - store calendar in logger queue specifics for multi-thread safety #837
+  - reenable default `init` method for `DDLogMessage` class #838
+- Added option to not copy messages #832
+- Added new hooks when adding loggers and formatters #836
+- Ability to create new log files every day #736
+- Skip messages in ASL logger which are filtered out by the formatter #786 #742
+- Fixed #823 by adding a `hash` implementation for `DDFileLogger` - same as `isEqual`, it only considers the `filePath` 7ceed08
+- Fix Travis CI build #807
+- Updated docs #798 #808 #811 #810 #820
+
+
+## [3.0.0 - Swift 3.0, Xcode 8 on Sep 21st, 2016](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.0.0)
+- Swift 3.0 and Xcode 8 support via #769, fixes #771 and #772. Many thanks to @ffried @max-potapov @chrisdoc @BarakRL @devxoul and the others who contributed
+
+
+## [2.4.0 - Swift 2.3 on Sep 19th, 2016](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.4.0)
+- Swift 2.3 explicit so that the project compiles on Xcode 8 - #747 #773 fix #762 #763 #766
+- CocoaPods 1.0.0 fully adopted - 0f5a793 637dfc1 70439fe #729
+- Fix CLIColor.h not found for non-AppKit binaries w/o clang modules #745
+- Retrieve the `DDLogLevel` of each logger associated to `DDLog` #753
+- updated doc: #727 a9f54c9 #741, diagrams in 8bd128d
+- Added CONTRIBUTING, ISSUE and PULL_REQUEST TEMPLATE and added a small Communication section to the Readme
+- Fixed an issue with one demo #760
+
+
+## [2.3.0 - Swift 2.2, Xcode7.3 on May 2nd, 2016](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.3.0)
+- Updated to Swift 2.2 - #704
+- replaced deprecated `__FUNCTION__`, `__FILE__`, `__LINE__` with newly added to Swift 2.2: `#function`, `#file`, `#line`
+- Xcode 7.3 update - #692 #662
+- simplify usage and integration of the static library target - #657
+- DDLog usable via instances - #679
+- Swift cleanup - #649
+- Enable Application extension API only for tvOS - #701
+- Added `appletvos` and `appletvsimulator` to `SUPPORTED_PLATFORMS` and set  `TVOS_DEPLOYMENT_TARGET` - #707
+- fixed `OSSpinLock` init issue - #653
+- Added check to prevent duplicate loggers - #682
+- fixed typo in import - #693
+- updated the docs - #646 #650 #656 #655 #661 #664 #667 #684 #724
+
+
+## [2.2.0 - TVOS, Xcode7.1 on Oct 28th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.2.0)
+- added `tvOS` support (thanks [@sinoru](https://github.com/sinoru)) - [#634](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/634) [#640](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/640) [#630](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/630) [#628](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/628) [#618](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/618) [#611](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/611)
+- Remove `(escaping)` from the Swift `@autoclosure` parameters - [#642](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/642)
+
+
+## [2.1.0 - Swift 2.0, WatchOS, Xcode7 on Oct 23rd, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.1.0)
+- Fixed the version for the Carthage builds - see [#633](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/633)
+- Improved documentation
+
+
+## [2.1.0 RC - Swift 2.0, WatchOS, Xcode7 on Oct 22nd, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.1.0-rc)
+- Refactored the `NSDateFormatter` related code to fix a bunch of issues: [#621](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/621)
+- Fix Issue [#488](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/488): Support `DDLog` without `AppKit Dependency` (`#define DD_CLI`): [#627](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/627)
+- Re-add `NS_DESIGNATED_INITIALIZER` [#619](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/619)
+
+
+## [2.1.0 Beta - Swift 2.0, WatchOS, Xcode7 on Oct 12th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.1.0-beta)
+- Updated the library to use Swift 2.0 and Xcode 7 [#617](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/617) [#545](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/545) [#534](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/534)
+- WatchOS support (2.0) [#583](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/583) [#581](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/581) [#579](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/579)
+
+
+## [2.0.3 Patch for 2.0.0 on Oct 13th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.3)
+
+- Compatibility with Xcode 6 that was broken by the 2.0.2 patch - [f042fd3](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/f042fd3)
+
+
+## [2.0.2 Patch for 2.0.0 on Oct 12th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.2)
+
+- Swift 1.2 fixes [#546](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/546) [#578](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/578) plus and update to Swift 2.0 [5627dff](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/5627dff) imported from our swift_2.0 branch
+- Make build work on `tvOS` [#597](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/597)
+- Make `CocoaLumberjackSwift-iOS` target depends on `CocoaLumberjack-iOS` [#575](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/575)
+- `APPLICATION_EXTENSION_API_ONLY` to `YES` for Extensions [#576](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/576)
+- Remove unnecessary `NS_DESIGNATED_INITIALIZER`s [#593](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/593) fixes [#592](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/592)
+- Add ignore warning mark for `DDMakeColor` [#553](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/553)
+- Kill unused function warnings from `DDTTYLogger.h` [#613](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/613)
+- Flag unused parameters as being unused to silence strict warnings [#566](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/566)
+- Extend ignore unused warning pragma to cover all platforms [#559](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/559)
+- Removed images.xcassets from Mobile project [#580](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/580)
+- Silence the Xcode 7 upgrade check - [#595](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/595)
+- Fix import for when CL framework files are manually imported into project [#560](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/560)
+- Don't override defines in case they're already set at project level [#551](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/551)
+- log full filepath when failing to set attribute [#550](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/550)
+- Fix issue in standalone build with `DDLegacyMacros.h` [#552](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/552)
+- Update `CustomFormatters.md` with proper thread-safe blurb [#555](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/555)
+- typo in parameter's variable name fixed [#568](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/568)
+- Typo: minor fix [#571](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/571)
+- Surely we should be adding 1, not 0 for `OSAtomicAdd32` ? [#587](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/587)
+- `rollLogFileWithCompletionBlock` calls back on background queue instead of main queue [#589](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/589)
+- Removing extraneous `\` on line 55 [#600](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/600)
+- Updated `GettingStarted.md` to include `ddLogLevel` [#602](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/602)
+- Remove redundant check for `processorCount` availability [#604](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/604)
+
+
+## [2.0.1 Patch for 2.0.0 on Jun 25th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.1)
+
+- **Carthage support** [#521](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/521) [#526](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/526)
+- fixed crash on `DDASLLogCapture` when `TIME` or `TIME_NSEC` is `NULL` [#484](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/484)
+- **Swift** fixes and improvements: [#483](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/483) [#509](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/509) [#518](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/518) [#522](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/522) [5eafceb](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/5eafceb)
+- Unit tests: [#500](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/500) [#498](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/498) [#499](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/499)
+- Fix [#478](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/478) by reverting [#473](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/473)
+- Add `armv7s` to static library [#538](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/538)
+- Fix `NSLog` `threadid` mismatch with iOS 8+/OSX 10.10+ [#514](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/514)
+- Fixed the `LogV` macros so that avalist is no longer undefined [#511](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/511)
+- Using type safe `DDColor` alias instead of #define directive [#506](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/506)
+- Several fixes/tweaks to `DDASLLogCapture` [#512](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/512)
+- Prevent duplicate log entries when both `DDASLLogCapture` and `DDASLLogger` are used [#515](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/515)
+- Fix memory leaks in `DDTTYLogger`, add self annotations to blocks [#536](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/536)
+- Update older syntax to modern subscripting for array access [#482](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/482)
+- Remove execute permission on non-executable files [#517](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/517)
+- Change code samples to use `DDLogFlagWarning` [#520](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/520)
+- Fix seemingly obvious typo in the `toLogLevel` function [#508](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/508)
+
+
+## [CocoaLumberjack 2.0.0 on Mar 13th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0)
+
+The library was strongly refactored, with a few goals in mind:
+- Swift support - that we will release in a separate milestone, since CocoaPods 0.36.0 just got out
+- Unit tests support
+- reorganised things (on disk)
+- better coding style
+
+See [Migration from 1.x to 2.x](https://github.com/CocoaLumberjack/CocoaLumberjack#migrating-to-2x)
+
+
+## [2.0.0-rc2 on Feb 20th, 2015](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-rc2)
+
+- Bucket of Swift improvements - [#434](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/434) [#437](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/437) [#449](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/449) [#440](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/440)
+- Fixed [#433](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/433) (build issue due to dispatch_queue properties) - [#455](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/455)
+- Enable codesign for iOS device framework builds - [#444](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/444)
+- Declare `automaticallyAppendNewlineForCustomFormatters` properties as `nonatomic` - [#443](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/443)
+- Warning fixes & type standardization - [#419](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/419)
+- Legacy checks updated - [#424](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/424)
+- Documentation updates
+
+
+## [2.0.0-rc on Dec 11th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-rc)
+
+- Fix `dispatch_queue_t` properties.
+- Fix `registeredClasses` crashes at launch.
+
+
+## [2.0.0-beta4 on Nov 7th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-beta4)
+
+- Major refactoring and clean up.
+- Remove superfluous `log` from property names and use underscore for direct variable access.
+- Preliminary Swift support through `CocoaLumberjack.swift`.
+- Automatic 1.9.x legacy support when `DDLog.h` is imported instead of the new `CocoaLumberjack.h`.
+
+
+## [2.0.0-beta3 on Oct 21st, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-beta3)
+
+- Modernize flag variables to be `NS_OPTIONS`/`NS_ENUM`.
+- Change the log flags and levels to `NSUInteger`.
+- Fix warning when compiled with assertions blocked.
+- Crash fixes.
+
+
+## [2.0.0-beta2 on Sep 30th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-beta2)
+
+- Cleanup code.
+- Match `NSLog` read UID functionality in `DDASLLogger`.
+- Update framework and static libraries.
+
+
+## [2.0.0 Beta on Aug 12th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/2.0.0-beta)
+
+See [Migrate from 1.x to 2.x](https://github.com/CocoaLumberjack/CocoaLumberjack#migrating-to-2x)
+
+
+## [1.9.2 Updated patch release for 1.9.0 on Aug 11th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.9.2)
+
+- Fixed `NSCalendar components:fromDate:` crash - [#140](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/140) [#307](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/307) [#216](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/216)
+- New `DDAssert` macros - [#306](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/306)
+- Limit log growth by disk space only, not the number of files - [#195](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/195) [#303](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/303)
+- Change the mechanism for adding new line character (i.e. '\n\) to log messages in some logger - [#308](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/308) [#310](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/310)
+- Fixed deprecations - [#320](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/320) [#312](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/312) [#317](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/317)
+- `aslmsg` not freed and causing memory leak - [#314](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/314)
+- Fixed `CompressingLogFileManager` compression bug - [#315](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/315)
+- Remove unnecessary `NULL` check before `free()` - [#316](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/316)
+
+
+## [1.9.1 Patch release for 1.9.0 on Jun 30th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.9.1)
+
+- Fixed issues in rolling frequency - [#243](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/243) [#295](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/295) [@georgekola](https://github.com/georgekola)
+- Fixed critical issue, `addLogger` method should use a full bit mask instead of `LOG_LEVEL_VERBOSE`, otherwise extended logs or extra flags are ignored [fe6824c](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/fe6824c) [@robbiehanson](https://github.com/robbiehanson)
+- Performance optimisation: use compiler macros to skip iOS version checks - [4656d3b](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/4656d3b) [#298](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/298) [#291](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/291) [@robbiehanson](https://github.com/robbiehanson) [@liviur](https://github.com/liviur)
+- Changed the `Build Active Architecture Only` to `NO` [#294](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/294) [#293](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/293)
+- Optimisation by reusing `NSDateFormatter` instances [#296](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/296) [#301](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/301)
+
+
+## [1.9.0 New ASL capture module, several File logger fixes on May 23rd, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.9.0)
+
+- New ASL capture module [#242](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/242) [#263](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/263)
+- Override default `NSFileProtection` handling [#285](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/285)
+- Replaced warnings when ARC was not enabled with errors [#284](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/284)
+- Fix for issue [#278](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/278) where really large log files can keep growing [#280](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/280)
+- Fixed Xcode warnings [#279](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/279)
+- Update `calendarUnitFlags` with new iOS SDK values [#277](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/277)
+- Fix possible crash in `[NSCalendar components:fromDate:]` [#277](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/277)
+- Fix [#262](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/262) inverted ifs when renaming log [#264](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/264)
+- Proper way of doing singletons (via `dispatch_once`) [#259](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/259)
+- Explicitly declare `DDFileLogger` and `DDDispatchQueueLogFormatter ` properties as atomic to avoid Xcode warnings [#258](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/258)
+- Set `NSFileProtectionKey` on the temporary file created during compression [#256](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/256)
+- Fix a rare crash in `CompressingLogFileManager` caused by an unchecked result from read [#255](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/255)
+- Add explicit casts for integer conversion [#253](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/253)
+- Replace use of `NSThread.detachNewThreadSelector` [#251](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/251)
+- Add a constructor override for `initWithLogsDirectory:` [#252](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/252)
+- Check and log the streamError whenever we fail to write during compression and log any failures when removing the original file or cleaning up the temporary file after compression failed [#250](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/250)
+- Following Apple's guidelines for iOS Static Libraries [#249](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/249)
+- Some extra warnings for the mobile framework xcode project [a2e5666](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/a2e5666)
+- Update `FineGrainedLoggingAppDelegate.m` [#244](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/244)
+- New `[DDLog log:message:]` primitive [7f8af2e](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/7f8af2e)
+- Fixed issue [#181](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/181) when logging messages in iOS7 devices aren't properly retrieved by `asl_search` [#240](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/240)
+- Allow prevention of log file reuse [#238](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/238)
+- `DDTTYLogger`: Favour XcodeColors environment variable [#237](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/237)
+- `DDLog`: calling `atexit_b` in CLI applications, that use Foundation framework [#234](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/234)
+
+
+## [1.8.1 AllLoggers and bugfixes on Feb 14th, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.8.1)
+
+- read access to all loggers - [#217](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/217) [#219](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/219)
+- fixed bug with archived logs not being handled correctly on iOS simulator - [#218](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/218)
+- log the `strerror(errno)` value when `setxattr()` fails - [#211](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/211)
+- Add a check for an archived log before overwriting - [#214](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/214)
+- improved safety by using assertions instead of comments (`DDLog` in the core) - [#221](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/221)
+- added Lumberjack logo :)
+
+
+## [1.8.0 Better CL support, custom logfile name format, bugfixes on Jan 21st, 2014](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.8.0)
+
+- `DDFileLogger` custom logfile (name) format - [#208](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/208)
+- Security static analysis fix - [#202](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/202)
+- `DDFileLogger`: using `CFBundleIdentifier` as a log filename prefix on OSX and iOS - [#206](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/206)
+- Allow disabling of specific levels per-logger - [#204](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/204)
+- Improve support for OS X command line tools - [#194](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/194)
+- `DDFileLogger`: fixed crash that occurred in case if application name == nil - [#198](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/198)
+- `DDFileLogger`: fixed comment - [#199](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/199)
+- Fix Travis - [#205](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/205)
+
+
+## [1.7.0 New log file naming convention and CocoaLumberjack organisation on Dec 10th, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.7.0)
+
+- new log file naming convention - [#191](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/191)
+- completed transition to **CocoaLumberjack** organisation - [#188](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/188)
+
+
+## [1.6.5.1 Patch release for Xcode 4.4+ compatibility on Dec 4th, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.5.1)
+
+- fixed compatibility with Xcode 4.4+ [#187](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/187)
+
+
+## [1.6.5 File Logger refactoring, Multi Formatter, preffixed extension classes on Dec 3rd, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.5)
+
+`DDFileLogger` refactoring and fixes (thanks [@dvor](https://github.com/dvor) and [@an0](https://github.com/an0)):
+- Fixed [#63](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/63) Loggers don't flush in Command Line Tool [#184](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/184)
+- Fixed [#52](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/52) Force log rotation [#183](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/183)
+- Fixed [#55](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/55) After deleting log file or log dir they aren't created again without relaunching the app [#183](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/183)
+- Fixed [#129](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/129) [iOS] `DDFileLogger` causes crash when logging from background app [#183](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/183)
+- Fixed [#153](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/153) Log file on iPhone only contains a single line [#177](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/177)
+- Fixed [#155](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/155) How do I combine all my log levels into one file? [#177](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/177)
+- Fixed [#175](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/175) `DFileLogger` `creationDate` bug on 64-bit iOS system [#177](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/177)
+- Allow customizing the naming convention for log files to use timestamps [#174](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/174)
+
+Other:
+- Implemented multiple formatter (`DDMultiFormatter` - alows chaining of formatters) [#178](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/178)
+- Added DD prefix to extension classes (`ContextFilterLogFormatter` and `DispatchQueueLogFormatter`) [#178](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/178)
+- Updated code indentation: Tabs changed to spaces [#180](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/180)
+- Included `DDLog+LOGV.h` in Cocoapods sources [d253bd7](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/d253bd7)
+- other fixes/improvements
+
+
+## [1.6.4 Fix compatibility with 3rd party frameworks on Nov 21st, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.4)
+
+* "Fix" conflicts with 3rd party libraries using `CocoaLumberjack` [#172](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/172)
+* Ignore deprecated warning for `dispatch_get_current_queue` [#167](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/167)
+* Add new `DEBUG` log level support to included loggers [#166](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/166)
+* Method declarations that make it easier to extend/modify `DispatchQueueLogFormatter` [#164](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/164)
+
+
+## [1.6.3 New macros, updated podspec and bug fixes on Apr 2nd, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.3)
+
+* Add `LOGV`-style macros [#161](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/161)
+* Fix getting queue's label [#159](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/159)
+* New log level `DEBUG` [#145](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/145)
+* Use `DISPATCH_CURRENT_QUEUE_LABEL` if available [#159](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/159)
+* Different `logLevel` per each logger [#151](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/151)
+* Created 2 subspecs, `Core` and `Extensions` [#152](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/152)
+* Updated observer for keypath using `NSStringFromSelector` + `@selector` [38e5da3](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/38e5da3)
+* Replaced `id` return type with `instancetype` [ebee454](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/ebee454)
+* Remove implicit conversion warnings [#149](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/149)
+* `DDTTYLogger`: Allow to set default color profiles for all contexts at once [#146](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/146) [#158](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/158)
+* `DDTTYLogger`: By default apply `setForegroundColor:backgroundColor:forFlag:` to `LOG_CONTEXT_ALL` [#154](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/154)
+* `DispatchQueueLogFormatter`: Use modern Objective-C [#142](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/142)
+* `DispatchQueueLogFormatter`: Make sure to always use a `NSGregorianCalendar` for date formatter [#142](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/142)
+* Replaced explicit reference to class name in `logFileWithPath` factory method [#131](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/131)
+* Catch exceptions in `logMessage:` [#130](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/130)
+* Fix enum type conversion warnings [#124](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/124)
+* Add deployment target condition for workaround [#121](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/121)
+* Fix static analyzer warnings about `nil` values in dictionary [#122](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/122)
+* Fix `dispatch_get_current_queue` crash [#121](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/121)
+* Fixing colors in greyscale color-space not working [d019cfd](https://github.com/CocoaLumberjack/CocoaLumberjack/commit/d019cfd)
+* Guard around `dispatch_resume()` being called with null pointer [#107](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/107)
+* `NULL` safety checks [#107](https://github.com/CocoaLumberjack/CocoaLumberjack/pull/107)
+
+
+## [1.6.2 on Apr 2nd, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.2)
+
+## [1.6.1 on Apr 2nd, 2013](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6.1)
+
+## [1.6 on Jul 3rd, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.6)
+
+## [1.5.1 on Jul 3rd, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.5.1)
+
+## [1.5 on Jul 3rd, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.5)
+
+## [1.4.1 on Jul 3rd, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.4.1)
+
+## [1.4 on Jul 3rd, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.4)
+
+## [1.3.3 on Mar 30th, 2012](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.3.3)
+
+## [1.3.2 on Dec 23rd, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.3.2)
+
+## [1.3.1 on Dec 9th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.3.1)
+
+## [1.3 on Dec 9th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.3)
+
+## [1.2.3 on Dec 9th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.2.3)
+
+## [1.2.2 on Dec 9th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.2.2)
+
+## [1.2.1 on Oct 13th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.2.1)
+
+## [1.2 on Oct 13th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.2)
+
+## [1.1 on Oct 13th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.1)
+
+## [1.0 on Oct 13th, 2011](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/1.0)

File diff suppressed because it is too large
+ 14 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/LICENSE


File diff suppressed because it is too large
+ 301 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/README.md


+ 57 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/CLI/CLIColor.m

@@ -0,0 +1,57 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if TARGET_OS_OSX
+
+#import <CocoaLumberjack/CLIColor.h>
+
+@interface CLIColor () {
+    CGFloat _red, _green, _blue, _alpha;
+}
+
+@end
+
+
+@implementation CLIColor
+
++ (instancetype)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
+    __auto_type color = [CLIColor new];
+    color->_red     = red;
+    color->_green   = green;
+    color->_blue    = blue;
+    color->_alpha   = alpha;
+    return color;
+}
+
+- (void)getRed:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha {
+    if (red) {
+        *red    = _red;
+    }
+    if (green) {
+        *green  = _green;
+    }
+    if (blue) {
+        *blue   = _blue;
+    }
+    if (alpha) {
+        *alpha  = _alpha;
+    }
+}
+
+@end
+
+#endif

+ 205 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m

@@ -0,0 +1,205 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if !TARGET_OS_WATCH
+
+#include <asl.h>
+#include <notify.h>
+#include <notify_keys.h>
+#include <sys/time.h>
+
+#import <CocoaLumberjack/DDASLLogCapture.h>
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+static __auto_type _cancel = YES;
+static __auto_type _captureLevel = DDLogLevelVerbose;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDASLLogCapture
+#pragma clang diagnostic pop
+
++ (void)start {
+    // Ignore subsequent calls
+    if (!_cancel) {
+        return;
+    }
+    
+    _cancel = NO;
+    
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+        [self captureAslLogs];
+    });
+}
+
++ (void)stop {
+    _cancel = YES;
+}
+
++ (DDLogLevel)captureLevel {
+    return _captureLevel;
+}
+
++ (void)setCaptureLevel:(DDLogLevel)level {
+    _captureLevel = level;
+}
+
+#pragma mark - Private methods
+
++ (void)configureAslQuery:(aslmsg)query {
+    const char param[] = "7";  // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter
+    
+    asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC);
+
+    // Don't retrieve logs from our own DDASLLogger
+    asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL);
+    
+#if !TARGET_OS_IPHONE || (defined(TARGET_SIMULATOR) && TARGET_SIMULATOR)
+    __auto_type processId = [[NSProcessInfo processInfo] processIdentifier];
+    char pid[16];
+    snprintf(pid, sizeof(pid), "%d", processId);
+    asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC);
+#endif
+}
+
++ (void)aslMessageReceived:(aslmsg)msg {
+    __auto_type messageCString = asl_get(msg, ASL_KEY_MSG);
+    if (messageCString == NULL)
+        return;
+
+    DDLogFlag flag;
+    BOOL async;
+
+    __auto_type levelCString = asl_get(msg, ASL_KEY_LEVEL);
+    switch (levelCString? atoi(levelCString) : 0) {
+        // By default all NSLog's with a ASL_LEVEL_WARNING level
+        case ASL_LEVEL_EMERG    :
+        case ASL_LEVEL_ALERT    :
+        case ASL_LEVEL_CRIT     : flag = DDLogFlagError;    async = NO;  break;
+        case ASL_LEVEL_ERR      : flag = DDLogFlagWarning;  async = YES; break;
+        case ASL_LEVEL_WARNING  : flag = DDLogFlagInfo;     async = YES; break;
+        case ASL_LEVEL_NOTICE   : flag = DDLogFlagDebug;    async = YES; break;
+        case ASL_LEVEL_INFO     :
+        case ASL_LEVEL_DEBUG    :
+        default                 : flag = DDLogFlagVerbose;  async = YES;  break;
+    }
+
+    if (!(_captureLevel & flag)) {
+        return;
+    }
+
+    //  NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding];
+    NSString *message = @(messageCString);
+
+    __auto_type secondsCString = asl_get(msg, ASL_KEY_TIME);
+    __auto_type nanoCString = asl_get(msg, ASL_KEY_TIME_NSEC);
+    __auto_type seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970;
+    __auto_type nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0;
+    __auto_type totalSeconds = seconds + (nanoSeconds / 1e9);
+
+    __auto_type timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
+
+    __auto_type logMessage = [[DDLogMessage alloc] initWithMessage:message
+                                                             level:_captureLevel
+                                                              flag:flag
+                                                           context:0
+                                                              file:@"DDASLLogCapture"
+                                                          function:nil
+                                                              line:0
+                                                               tag:nil
+                                                           options:DDLogMessageDontCopyMessage
+                                                         timestamp:timeStamp];
+
+    [DDLog log:async message:logMessage];
+}
+
++ (void)captureAslLogs {
+    @autoreleasepool
+    {
+        /*
+           We use ASL_KEY_MSG_ID to see each message once, but there's no
+           obvious way to get the "next" ID. To bootstrap the process, we'll
+           search by timestamp until we've seen a message.
+         */
+
+        struct timeval timeval = {
+            .tv_sec = 0
+        };
+        gettimeofday(&timeval, NULL);
+        __auto_type startTime = (unsigned long long)timeval.tv_sec;
+        __block unsigned long long lastSeenID = 0;
+
+        /*
+           syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message)
+           through the notify API when it saves messages to the ASL database.
+           There is some coalescing - currently it is sent at most twice per
+           second - but there is no documented guarantee about this. In any
+           case, there may be multiple messages per notification.
+
+           Notify notifications don't carry any payload, so we need to search
+           for the messages.
+         */
+        int notifyToken = 0;  // Can be used to unregister with notify_cancel().
+        notify_register_dispatch(kNotifyASLDBUpdate, &notifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
+        {
+            // At least one message has been posted; build a search query.
+            @autoreleasepool
+            {
+                __auto_type query = asl_new(ASL_TYPE_QUERY);
+                char stringValue[64];
+
+                if (lastSeenID > 0) {
+                    snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID);
+                    asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC);
+                } else {
+                    snprintf(stringValue, sizeof stringValue, "%llu", startTime);
+                    asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC);
+                }
+
+                [self configureAslQuery:query];
+
+                // Iterate over new messages.
+                aslmsg msg;
+                __auto_type response = asl_search(NULL, query);
+
+                while ((msg = asl_next(response)))
+                {
+                    [self aslMessageReceived:msg];
+
+                    // Keep track of which messages we've seen.
+                    lastSeenID = (unsigned long long)atoll(asl_get(msg, ASL_KEY_MSG_ID));
+                }
+                asl_release(response);
+                asl_free(query);
+
+                if (_cancel) {
+                    notify_cancel(token);
+                    return;
+                }
+
+            }
+        });
+    }
+}
+
+@end
+
+#endif

+ 133 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m

@@ -0,0 +1,133 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+
+#if !TARGET_OS_WATCH
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <asl.h>
+
+#import <CocoaLumberjack/DDASLLogger.h>
+
+const char* const kDDASLKeyDDLog = "DDLog";
+const char* const kDDASLDDLogValue = "1";
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+static DDASLLogger *sharedInstance;
+#pragma clang diagnostic pop
+
+@interface DDASLLogger () {
+    aslclient _client;
+}
+
+@end
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDASLLogger
+#pragma clang diagnostic pop
+
++ (instancetype)sharedInstance {
+    static dispatch_once_t DDASLLoggerOnceToken;
+
+    dispatch_once(&DDASLLoggerOnceToken, ^{
+        sharedInstance = [[[self class] alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+- (instancetype)init {
+    if (sharedInstance != nil) {
+        return nil;
+    }
+
+    if ((self = [super init])) {
+        // A default asl client is provided for the main thread,
+        // but background threads need to create their own client.
+
+        _client = asl_open(NULL, "com.apple.console", 0);
+    }
+
+    return self;
+}
+
+- (DDLoggerName)loggerName {
+    return DDLoggerNameASL;
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Skip captured log messages
+    if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
+        return;
+    }
+
+    __auto_type message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
+
+    if (message) {
+        __auto_type msg = [message UTF8String];
+
+        size_t aslLogLevel;
+        switch (logMessage->_flag) {
+            // Note: By default ASL will filter anything above level 5 (Notice).
+            // So our mappings shouldn't go above that level.
+            case DDLogFlagError     : aslLogLevel = ASL_LEVEL_CRIT;     break;
+            case DDLogFlagWarning   : aslLogLevel = ASL_LEVEL_ERR;      break;
+            case DDLogFlagInfo      : aslLogLevel = ASL_LEVEL_WARNING;  break; // Regular NSLog's level
+            case DDLogFlagDebug     :
+            case DDLogFlagVerbose   :
+            default                 : aslLogLevel = ASL_LEVEL_NOTICE;   break;
+        }
+
+        static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" };
+
+        // NSLog uses the current euid to set the ASL_KEY_READ_UID.
+        const __auto_type readUID = geteuid();
+
+        char readUIDString[16];
+#ifndef NS_BLOCK_ASSERTIONS
+        __auto_type l = (size_t)snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
+#else
+        snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
+#endif
+
+        NSAssert(l < sizeof(readUIDString),
+                 @"Formatted euid is too long.");
+        NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])),
+                 @"Unhandled ASL log level.");
+
+        __auto_type m = asl_new(ASL_TYPE_MSG);
+        if (m != NULL) {
+            if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 &&
+                asl_set(m, ASL_KEY_MSG, msg) == 0 &&
+                asl_set(m, ASL_KEY_READ_UID, readUIDString) == 0 &&
+                asl_set(m, kDDASLKeyDDLog, kDDASLDDLogValue) == 0) {
+                asl_send(_client, m);
+            }
+            asl_free(m);
+        }
+        //TODO handle asl_* failures non-silently?
+    }
+}
+
+@end
+
+#endif

+ 636 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m

@@ -0,0 +1,636 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <CocoaLumberjack/DDAbstractDatabaseLogger.h>
+
+@interface DDAbstractDatabaseLogger ()
+
+- (void)destroySaveTimer;
+- (void)updateAndResumeSaveTimer;
+- (void)createSuspendedSaveTimer;
+- (void)destroyDeleteTimer;
+- (void)updateDeleteTimer;
+- (void)createAndStartDeleteTimer;
+
+@end
+
+#pragma mark -
+
+@implementation DDAbstractDatabaseLogger
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _saveThreshold = 500;
+        _saveInterval = 60;           // 60 seconds
+        _maxAge = (60 * 60 * 24 * 7); //  7 days
+        _deleteInterval = (60 * 5);   //  5 minutes
+    }
+
+    return self;
+}
+
+- (void)dealloc {
+    [self destroySaveTimer];
+    [self destroyDeleteTimer];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Override Me
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)db_log:(__unused DDLogMessage *)logMessage {
+    // Override me and add your implementation.
+    //
+    // Return YES if an item was added to the buffer.
+    // Return NO if the logMessage was ignored.
+
+    return NO;
+}
+
+- (void)db_save {
+    // Override me and add your implementation.
+}
+
+- (void)db_delete {
+    // Override me and add your implementation.
+}
+
+- (void)db_saveAndDelete {
+    // Override me and add your implementation.
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Private API
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)performSaveAndSuspendSaveTimer {
+    if (_unsavedCount > 0) {
+        if (_deleteOnEverySave) {
+            [self db_saveAndDelete];
+        } else {
+            [self db_save];
+        }
+    }
+
+    _unsavedCount = 0;
+    _unsavedTime = 0;
+
+    if (_saveTimer != NULL && _saveTimerSuspended == 0) {
+        dispatch_suspend(_saveTimer);
+        _saveTimerSuspended = 1;
+    }
+}
+
+- (void)performDelete {
+    if (_maxAge > 0.0) {
+        [self db_delete];
+
+        _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Timers
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)destroySaveTimer {
+    if (_saveTimer != NULL) {
+        dispatch_source_cancel(_saveTimer);
+
+        // Must activate a timer before releasing it (or it will crash)
+        if (_saveTimerSuspended < 0) {
+            dispatch_activate(_saveTimer);
+        } else if (_saveTimerSuspended > 0) {
+            dispatch_resume(_saveTimer);
+        }
+
+#if !OS_OBJECT_USE_OBJC
+        dispatch_release(_saveTimer);
+#endif
+        _saveTimer = NULL;
+        _saveTimerSuspended = 0;
+    }
+}
+
+- (void)updateAndResumeSaveTimer {
+    if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0)) {
+        __auto_type interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
+        __auto_type startTime = dispatch_time(_unsavedTime, (int64_t)interval);
+
+        dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC);
+
+        if (_saveTimerSuspended < 0) {
+            dispatch_activate(_saveTimer);
+            _saveTimerSuspended = 0;
+        } else if (_saveTimerSuspended > 0) {
+            dispatch_resume(_saveTimer);
+            _saveTimerSuspended = 0;
+        }
+    }
+}
+
+- (void)createSuspendedSaveTimer {
+    if ((_saveTimer == NULL) && (_saveInterval > 0.0)) {
+        _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
+
+        dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
+            [self performSaveAndSuspendSaveTimer];
+        } });
+
+        _saveTimerSuspended = -1;
+    }
+}
+
+- (void)destroyDeleteTimer {
+    if (_deleteTimer != NULL) {
+        dispatch_source_cancel(_deleteTimer);
+#if !OS_OBJECT_USE_OBJC
+        dispatch_release(_deleteTimer);
+#endif
+        _deleteTimer = NULL;
+    }
+}
+
+- (void)updateDeleteTimer {
+    if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
+        __auto_type interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
+        dispatch_time_t startTime;
+
+        if (_lastDeleteTime > 0) {
+            startTime = dispatch_time(_lastDeleteTime, interval);
+        } else {
+            startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
+        }
+
+        dispatch_source_set_timer(_deleteTimer, startTime, (uint64_t)interval, 1ull * NSEC_PER_SEC);
+    }
+}
+
+- (void)createAndStartDeleteTimer {
+    if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
+        _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
+
+        if (_deleteTimer != NULL) {
+            dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
+                [self performDelete];
+            } });
+
+            [self updateDeleteTimer];
+
+            // We are sure that -updateDeleteTimer did call dispatch_source_set_timer()
+            // since it has the same guards on _deleteInterval and _maxAge
+            dispatch_activate(_deleteTimer);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSUInteger)saveThreshold {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    DDAbstractLoggerAssertLockedPropertyAccess();
+
+    __block NSUInteger result;
+    dispatch_sync(DDLog.loggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_saveThreshold;
+        });
+    });
+
+    return result;
+}
+
+- (void)setSaveThreshold:(NSUInteger)threshold {
+    dispatch_block_t block = ^{
+        @autoreleasepool {
+            if (self->_saveThreshold != threshold) {
+                self->_saveThreshold = threshold;
+
+                // Since the saveThreshold has changed,
+                // we check to see if the current unsavedCount has surpassed the new threshold.
+                //
+                // If it has, we immediately save the log.
+
+                if ((self->_unsavedCount >= self->_saveThreshold) && (self->_saveThreshold > 0)) {
+                    [self performSaveAndSuspendSaveTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
+        dispatch_async(DDLog.loggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)saveInterval {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    DDAbstractLoggerAssertLockedPropertyAccess();
+
+    __block NSTimeInterval result;
+    dispatch_sync(DDLog.loggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_saveInterval;
+        });
+    });
+
+    return result;
+}
+
+- (void)setSaveInterval:(NSTimeInterval)interval {
+    __auto_type block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* saveInterval != interval */ islessgreater(self->_saveInterval, interval)) {
+                self->_saveInterval = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the saveInterval was previously enabled and it just got disabled,
+                //    then we need to stop the saveTimer. (And we might as well release it.)
+                //
+                // 2. If the saveInterval was previously disabled and it just got enabled,
+                //    then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
+                //
+                // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
+                //
+                // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
+                //    (Plus we might need to do an immediate save.)
+
+                if (self->_saveInterval > 0.0) {
+                    if (self->_saveTimer == NULL) {
+                        // Handles #2
+                        //
+                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self createSuspendedSaveTimer];
+                        [self updateAndResumeSaveTimer];
+                    } else {
+                        // Handles #3
+                        // Handles #4
+                        //
+                        // Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self updateAndResumeSaveTimer];
+                    }
+                } else if (self->_saveTimer) {
+                    // Handles #1
+
+                    [self destroySaveTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
+        dispatch_async(DDLog.loggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)maxAge {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    DDAbstractLoggerAssertLockedPropertyAccess();
+
+    __block NSTimeInterval result;
+
+    dispatch_sync(DDLog.loggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_maxAge;
+        });
+    });
+
+    return result;
+}
+
+- (void)setMaxAge:(NSTimeInterval)interval {
+    __auto_type block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) {
+                __auto_type oldMaxAge = self->_maxAge;
+                __auto_type newMaxAge = interval;
+
+                self->_maxAge = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the maxAge was previously enabled and it just got disabled,
+                //    then we need to stop the deleteTimer. (And we might as well release it.)
+                //
+                // 2. If the maxAge was previously disabled and it just got enabled,
+                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
+                //
+                // 3. If the maxAge was increased,
+                //    then we don't need to do anything.
+                //
+                // 4. If the maxAge was decreased,
+                //    then we should do an immediate delete.
+
+                __auto_type shouldDeleteNow = NO;
+
+                if (oldMaxAge > 0.0) {
+                    if (newMaxAge <= 0.0) {
+                        // Handles #1
+
+                        [self destroyDeleteTimer];
+                    } else if (oldMaxAge > newMaxAge) {
+                        // Handles #4
+                        shouldDeleteNow = YES;
+                    }
+                } else if (newMaxAge > 0.0) {
+                    // Handles #2
+                    shouldDeleteNow = YES;
+                }
+
+                if (shouldDeleteNow) {
+                    [self performDelete];
+
+                    if (self->_deleteTimer) {
+                        [self updateDeleteTimer];
+                    } else {
+                        [self createAndStartDeleteTimer];
+                    }
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
+        dispatch_async(DDLog.loggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (NSTimeInterval)deleteInterval {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    DDAbstractLoggerAssertLockedPropertyAccess();
+
+    __block NSTimeInterval result;
+
+    dispatch_sync(DDLog.loggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_deleteInterval;
+        });
+    });
+
+    return result;
+}
+
+- (void)setDeleteInterval:(NSTimeInterval)interval {
+    __auto_type block = ^{
+        @autoreleasepool {
+            // C99 recommended floating point comparison macro
+            // Read: isLessThanOrGreaterThan(floatA, floatB)
+
+            if (/* deleteInterval != interval */ islessgreater(self->_deleteInterval, interval)) {
+                self->_deleteInterval = interval;
+
+                // There are several cases we need to handle here.
+                //
+                // 1. If the deleteInterval was previously enabled and it just got disabled,
+                //    then we need to stop the deleteTimer. (And we might as well release it.)
+                //
+                // 2. If the deleteInterval was previously disabled and it just got enabled,
+                //    then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
+                //
+                // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
+                //
+                // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
+                //    (Plus we might need to do an immediate delete.)
+
+                if (self->_deleteInterval > 0.0) {
+                    if (self->_deleteTimer == NULL) {
+                        // Handles #2
+                        //
+                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
+                        // if a delete is needed the timer will fire immediately.
+
+                        [self createAndStartDeleteTimer];
+                    } else {
+                        // Handles #3
+                        // Handles #4
+                        //
+                        // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
+                        // if a save is needed the timer will fire immediately.
+
+                        [self updateDeleteTimer];
+                    }
+                } else if (self->_deleteTimer) {
+                    // Handles #1
+
+                    [self destroyDeleteTimer];
+                }
+            }
+        }
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
+
+        dispatch_async(DDLog.loggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+- (BOOL)deleteOnEverySave {
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    // Note: The internal implementation MUST access the colorsEnabled variable directly,
+    // This method is designed explicitly for external access.
+    //
+    // Using "self." syntax to go through this method will cause immediate deadlock.
+    // This is the intended result. Fix it by accessing the ivar directly.
+    // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
+
+    DDAbstractLoggerAssertLockedPropertyAccess();
+
+    __block BOOL result;
+
+    dispatch_sync(DDLog.loggingQueue, ^{
+        dispatch_sync(self.loggerQueue, ^{
+            result = self->_deleteOnEverySave;
+        });
+    });
+
+    return result;
+}
+
+- (void)setDeleteOnEverySave:(BOOL)flag {
+    __auto_type block = ^{
+        self->_deleteOnEverySave = flag;
+    };
+
+    // The design of the setter logic below is taken from the DDAbstractLogger implementation.
+    // For documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
+        dispatch_async(DDLog.loggingQueue, ^{
+            dispatch_async(self.loggerQueue, block);
+        });
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Public API
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)savePendingLogEntries {
+    __auto_type block = ^{
+        @autoreleasepool {
+            [self performSaveAndSuspendSaveTimer];
+        }
+    };
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_async(self.loggerQueue, block);
+    }
+}
+
+- (void)deleteOldLogEntries {
+    __auto_type block = ^{
+        @autoreleasepool {
+            [self performDelete];
+        }
+    };
+
+    if ([self isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_async(self.loggerQueue, block);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark DDLogger
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)didAddLogger {
+    // If you override me be sure to invoke [super didAddLogger];
+    [self createSuspendedSaveTimer];
+    [self createAndStartDeleteTimer];
+}
+
+- (void)willRemoveLogger {
+    // If you override me be sure to invoke [super willRemoveLogger];
+    [self performSaveAndSuspendSaveTimer];
+    [self destroySaveTimer];
+    [self destroyDeleteTimer];
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    if ([self db_log:logMessage]) {
+        __auto_type firstUnsavedEntry = (++_unsavedCount == 1);
+
+        if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
+            [self performSaveAndSuspendSaveTimer];
+        } else if (firstUnsavedEntry) {
+            _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
+            [self updateAndResumeSaveTimer];
+        }
+    }
+}
+
+- (void)flush {
+    // This method is invoked by DDLog's flushLog method.
+    //
+    // It is called automatically when the application quits,
+    // or if the developer invokes DDLog's flushLog method prior to crashing or something.
+    [self performSaveAndSuspendSaveTimer];
+}
+
+@end

+ 31 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger+Internal.h

@@ -0,0 +1,31 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDFileLogger.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DDFileLogger (Internal)
+
+- (void)logData:(NSData *)data;
+
+// Will assert if used outside logger's queue.
+- (void)lt_logData:(NSData *)data;
+
+- (nullable NSData *)lt_dataForMessage:(DDLogMessage *)message;
+
+@end
+
+NS_ASSUME_NONNULL_END

File diff suppressed because it is too large
+ 1865 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m


File diff suppressed because it is too large
+ 1321 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m


+ 21 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDLoggerNames.m

@@ -0,0 +1,21 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDLoggerNames.h>
+
+DDLoggerName const DDLoggerNameASL    = @"cocoa.lumberjack.aslLogger";
+DDLoggerName const DDLoggerNameTTY    = @"cocoa.lumberjack.ttyLogger";
+DDLoggerName const DDLoggerNameOS     = @"cocoa.lumberjack.osLogger";
+DDLoggerName const DDLoggerNameFile   = @"cocoa.lumberjack.fileLogger";

+ 158 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m

@@ -0,0 +1,158 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <TargetConditionals.h>
+#import <os/log.h>
+
+#import <CocoaLumberjack/DDOSLogger.h>
+
+@implementation DDOSLogLevelMapperDefault
+
+- (instancetype)init {
+    self = [super init];
+    return self;
+}
+
+- (os_log_type_t)osLogTypeForLogFlag:(DDLogFlag)logFlag {
+    switch (logFlag) {
+        case DDLogFlagError:
+        case DDLogFlagWarning:
+            return OS_LOG_TYPE_ERROR;
+        case DDLogFlagInfo:
+            return OS_LOG_TYPE_INFO;
+        case DDLogFlagDebug:
+        case DDLogFlagVerbose:
+            return OS_LOG_TYPE_DEBUG;
+        default:
+            return OS_LOG_TYPE_DEFAULT;
+    }
+}
+
+@end
+
+#if TARGET_OS_SIMULATOR
+@implementation DDOSLogLevelMapperSimulatorConsoleAppWorkaround
+
+- (os_log_type_t)osLogTypeForLogFlag:(DDLogFlag)logFlag {
+    __auto_type defaultMapping = [super osLogTypeForLogFlag:logFlag];
+    return (defaultMapping == OS_LOG_TYPE_DEBUG) ? OS_LOG_TYPE_DEFAULT : defaultMapping;
+}
+
+@end
+#endif
+
+@interface DDOSLogger ()
+
+@property (nonatomic, copy, readonly, nullable) NSString *subsystem;
+@property (nonatomic, copy, readonly, nullable) NSString *category;
+@property (nonatomic, strong, readonly, nonnull) os_log_t logger;
+
+@end
+
+@implementation DDOSLogger
+
+@synthesize subsystem = _subsystem;
+@synthesize category = _category;
+@synthesize logLevelMapper = _logLevelMapper;
+@synthesize logger = _logger;
+
+#pragma mark - Shared Instance
+
+API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
+static DDOSLogger *sharedInstance;
+
++ (instancetype)sharedInstance {
+    static dispatch_once_t DDOSLoggerOnceToken;
+
+    dispatch_once(&DDOSLoggerOnceToken, ^{
+        sharedInstance = [[[self class] alloc] init];
+    });
+
+    return sharedInstance;
+}
+
+#pragma mark - Initialization
+- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category {
+    NSAssert((subsystem == nil) == (category == nil), 
+             @"Either both subsystem and category or neither should be nil.");
+    if (self = [super init]) {
+        _subsystem = [subsystem copy];
+        _category = [category copy];
+        _logLevelMapper = [[DDOSLogLevelMapperDefault alloc] init];
+    }
+    return self;
+}
+
+- (instancetype)init {
+    return [self initWithSubsystem:nil category:nil];
+}
+
+- (instancetype)initWithSubsystem:(NSString *)subsystem
+                         category:(NSString *)category
+                   logLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper {
+    if (self = [self initWithSubsystem:subsystem category:category]) {
+        NSParameterAssert(logLevelMapper);
+        _logLevelMapper = logLevelMapper;
+    }
+    return self;
+}
+
+- (instancetype)initWithLogLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper {
+    return [self initWithSubsystem:nil category:nil logLevelMapper:logLevelMapper];
+}
+
+#pragma mark - Mapper
+- (id<DDOSLogLevelMapper>)logLevelMapper {
+    if (_logLevelMapper == nil) {
+        _logLevelMapper = [[DDOSLogLevelMapperDefault alloc] init];
+    }
+    return _logLevelMapper;
+}
+
+#pragma mark - os_log
+- (os_log_t)logger {
+    if (_logger == nil)  {
+        if (self.subsystem == nil || self.category == nil) {
+            _logger = OS_LOG_DEFAULT;
+        } else {
+            _logger = os_log_create(self.subsystem.UTF8String, self.category.UTF8String);
+        }
+    }
+    return _logger;
+}
+
+#pragma mark - DDLogger
+- (DDLoggerName)loggerName {
+    return DDLoggerNameOS;
+}
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+#if !TARGET_OS_WATCH // See DDASLLogCapture.m -> Was never supported on watchOS.
+    // Skip captured log messages.
+    if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
+        return;
+    }
+#endif
+
+    if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) {
+        __auto_type message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
+        if (message != nil) {
+            __auto_type logType = [self.logLevelMapper osLogTypeForLogFlag:logMessage->_flag];
+            os_log_with_type(self.logger, logType, "%{public}s", message.UTF8String);
+        }
+    }
+}
+
+@end

File diff suppressed because it is too large
+ 1446 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m


+ 57 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m

@@ -0,0 +1,57 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h>
+
+@implementation DDContextAllowlistFilterLogFormatter (Deprecated)
+
+- (void)addToWhitelist:(NSInteger)loggingContext {
+    [self addToAllowlist:loggingContext];
+}
+
+- (void)removeFromWhitelist:(NSInteger)loggingContext {
+    [self removeFromAllowlist:loggingContext];
+}
+
+- (NSArray *)whitelist {
+    return [self allowlist];
+}
+
+- (BOOL)isOnWhitelist:(NSInteger)loggingContext {
+    return [self isOnAllowlist:loggingContext];
+}
+
+@end
+
+
+@implementation DDContextDenylistFilterLogFormatter (Deprecated)
+
+- (void)addToBlacklist:(NSInteger)loggingContext {
+    [self addToDenylist:loggingContext];
+}
+
+- (void)removeFromBlacklist:(NSInteger)loggingContext {
+    [self removeFromDenylist:loggingContext];
+}
+
+- (NSArray *)blacklist {
+    return [self denylist];
+}
+
+- (BOOL)isOnBlacklist:(NSInteger)loggingContext {
+    return [self isOnDenylist:loggingContext];
+}
+
+@end

+ 185 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m

@@ -0,0 +1,185 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <pthread/pthread.h>
+
+#import <CocoaLumberjack/DDContextFilterLogFormatter.h>
+
+@interface DDLoggingContextSet : NSObject
+
+@property (readonly, copy, nonnull) NSArray *currentSet;
+
+- (void)addToSet:(NSInteger)loggingContext;
+- (void)removeFromSet:(NSInteger)loggingContext;
+
+- (BOOL)isInSet:(NSInteger)loggingContext;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDContextAllowlistFilterLogFormatter () {
+    DDLoggingContextSet *_contextSet;
+}
+@end
+
+@implementation DDContextAllowlistFilterLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _contextSet = [[DDLoggingContextSet alloc] init];
+    }
+    return self;
+}
+
+- (void)addToAllowlist:(NSInteger)loggingContext {
+    [_contextSet addToSet:loggingContext];
+}
+
+- (void)removeFromAllowlist:(NSInteger)loggingContext {
+    [_contextSet removeFromSet:loggingContext];
+}
+
+- (NSArray *)allowlist {
+    return [_contextSet currentSet];
+}
+
+- (BOOL)isOnAllowlist:(NSInteger)loggingContext {
+    return [_contextSet isInSet:loggingContext];
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    if ([self isOnAllowlist:logMessage->_context]) {
+        return logMessage->_message;
+    } else {
+        return nil;
+    }
+}
+
+@end
+
+
+@interface DDContextDenylistFilterLogFormatter () {
+    DDLoggingContextSet *_contextSet;
+}
+@end
+
+@implementation DDContextDenylistFilterLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _contextSet = [[DDLoggingContextSet alloc] init];
+    }
+    return self;
+}
+
+- (void)addToDenylist:(NSInteger)loggingContext {
+    [_contextSet addToSet:loggingContext];
+}
+
+- (void)removeFromDenylist:(NSInteger)loggingContext {
+    [_contextSet removeFromSet:loggingContext];
+}
+
+- (NSArray *)denylist {
+    return [_contextSet currentSet];
+}
+
+- (BOOL)isOnDenylist:(NSInteger)loggingContext {
+    return [_contextSet isInSet:loggingContext];
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    if ([self isOnDenylist:logMessage->_context]) {
+        return nil;
+    } else {
+        return logMessage->_message;
+    }
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface DDLoggingContextSet () {
+    pthread_mutex_t _mutex;
+    NSMutableSet *_set;
+}
+@end
+
+@implementation DDLoggingContextSet
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _set = [[NSMutableSet alloc] init];
+        pthread_mutex_init(&_mutex, NULL);
+    }
+
+    return self;
+}
+
+- (void)dealloc {
+    pthread_mutex_destroy(&_mutex);
+}
+
+- (void)addToSet:(NSInteger)loggingContext {
+    pthread_mutex_lock(&_mutex);
+    {
+        [_set addObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+- (void)removeFromSet:(NSInteger)loggingContext {
+    pthread_mutex_lock(&_mutex);
+    {
+        [_set removeObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+- (NSArray *)currentSet {
+    NSArray *result = nil;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = [_set allObjects];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+- (BOOL)isInSet:(NSInteger)loggingContext {
+    __auto_type result = NO;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = [_set containsObject:@(loggingContext)];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+@end

+ 240 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m

@@ -0,0 +1,240 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <pthread/pthread.h>
+#import <stdatomic.h>
+#import <sys/qos.h>
+
+#import <CocoaLumberjack/DDDispatchQueueLogFormatter.h>
+
+DDQualityOfServiceName const DDQualityOfServiceUserInteractive = @"UI";
+DDQualityOfServiceName const DDQualityOfServiceUserInitiated   = @"IN";
+DDQualityOfServiceName const DDQualityOfServiceDefault         = @"DF";
+DDQualityOfServiceName const DDQualityOfServiceUtility         = @"UT";
+DDQualityOfServiceName const DDQualityOfServiceBackground      = @"BG";
+DDQualityOfServiceName const DDQualityOfServiceUnspecified     = @"UN";
+
+static DDQualityOfServiceName _qos_name(NSUInteger qos) {
+    switch ((qos_class_t) qos) {
+        case QOS_CLASS_USER_INTERACTIVE: return DDQualityOfServiceUserInteractive;
+        case QOS_CLASS_USER_INITIATED:   return DDQualityOfServiceUserInitiated;
+        case QOS_CLASS_DEFAULT:          return DDQualityOfServiceDefault;
+        case QOS_CLASS_UTILITY:          return DDQualityOfServiceUtility;
+        case QOS_CLASS_BACKGROUND:       return DDQualityOfServiceBackground;
+        default:                         return DDQualityOfServiceUnspecified;
+    }
+}
+
+#pragma mark - DDDispatchQueueLogFormatter
+
+@interface DDDispatchQueueLogFormatter () {
+    NSDateFormatter *_dateFormatter;      // Use [self stringFromDate]
+
+    pthread_mutex_t _mutex;
+
+    NSUInteger _minQueueLength;           // _prefix == Only access via atomic property
+    NSUInteger _maxQueueLength;           // _prefix == Only access via atomic property
+    NSMutableDictionary *_replacements;   // _prefix == Only access from within spinlock
+}
+@end
+
+
+@implementation DDDispatchQueueLogFormatter
+
+- (instancetype)init {
+    if ((self = [super init])) {
+        _dateFormatter = [self createDateFormatter];
+
+        pthread_mutex_init(&_mutex, NULL);
+        _replacements = [[NSMutableDictionary alloc] init];
+
+        // Set default replacements:
+        _replacements[@"com.apple.main-thread"] = @"main";
+    }
+
+    return self;
+}
+
+- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode {
+    return [self init];
+}
+
+- (void)dealloc {
+    pthread_mutex_destroy(&_mutex);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@synthesize minQueueLength = _minQueueLength;
+@synthesize maxQueueLength = _maxQueueLength;
+
+- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel {
+    NSString *result = nil;
+
+    pthread_mutex_lock(&_mutex);
+    {
+        result = _replacements[longLabel];
+    }
+    pthread_mutex_unlock(&_mutex);
+
+    return result;
+}
+
+- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel {
+    pthread_mutex_lock(&_mutex);
+    {
+        if (shortLabel) {
+            _replacements[longLabel] = shortLabel;
+        } else {
+            [_replacements removeObjectForKey:longLabel];
+        }
+    }
+    pthread_mutex_unlock(&_mutex);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark DDLogFormatter
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSDateFormatter *)createDateFormatter {
+    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
+    [self configureDateFormatter:formatter];
+    return formatter;
+}
+
+- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter {
+    [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
+    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"];
+    [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
+    [dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]];
+}
+
+- (NSString *)stringFromDate:(NSDate *)date {
+    return [_dateFormatter stringFromDate:date];
+}
+
+- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage {
+    // As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
+
+    __auto_type useQueueLabel = NO;
+    if (logMessage->_queueLabel) {
+        useQueueLabel = YES;
+
+        // If you manually create a thread, it's dispatch_queue will have one of the thread names below.
+        // Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
+        const NSArray<NSString *> *names = @[
+            @"com.apple.root.low-priority",
+            @"com.apple.root.default-priority",
+            @"com.apple.root.high-priority",
+            @"com.apple.root.low-overcommit-priority",
+            @"com.apple.root.default-overcommit-priority",
+            @"com.apple.root.high-overcommit-priority",
+            @"com.apple.root.default-qos.overcommit",
+        ];
+        for (NSString *name in names) {
+            if ([logMessage->_queueLabel isEqualToString:name]) {
+                useQueueLabel = NO;
+                break;
+            }
+        }
+    }
+
+    // Get the name of the queue, thread, or machID (whichever we are to use).
+    NSString *queueThreadLabel;
+    if (useQueueLabel || [logMessage->_threadName length] > 0) {
+        __auto_type fullLabel = useQueueLabel ? logMessage->_queueLabel : logMessage->_threadName;
+
+        NSString *abrvLabel;
+        pthread_mutex_lock(&_mutex);
+        {
+            abrvLabel = _replacements[fullLabel];
+        }
+        pthread_mutex_unlock(&_mutex);
+
+        queueThreadLabel = abrvLabel ?: fullLabel;
+    } else {
+        queueThreadLabel = logMessage->_threadID;
+    }
+
+    // Now use the thread label in the output
+    // labelLength > maxQueueLength : truncate
+    // labelLength < minQueueLength : padding
+    //                              : exact
+    __auto_type minQueueLength = self.minQueueLength;
+    __auto_type maxQueueLength = self.maxQueueLength;
+    __auto_type labelLength = [queueThreadLabel length];
+    if (maxQueueLength > 0 && labelLength > maxQueueLength) {
+        // Truncate
+        return [queueThreadLabel substringToIndex:maxQueueLength];
+    } else if (labelLength < minQueueLength) {
+        // Padding
+        return [queueThreadLabel stringByPaddingToLength:minQueueLength
+                                              withString:@" "
+                                         startingAtIndex:0];
+    } else {
+        // Exact
+        return queueThreadLabel;
+    }
+}
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    __auto_type timestamp = [self stringFromDate:logMessage->_timestamp];
+    __auto_type queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
+
+    return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message];
+}
+
+@end
+
+#pragma mark - DDAtomicCounter
+
+@interface DDAtomicCounter() {
+    atomic_int_fast32_t _value;
+}
+@end
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+@implementation DDAtomicCounter
+#pragma clang diagnostic pop
+
+- (instancetype)initWithDefaultValue:(int32_t)defaultValue {
+    if ((self = [super init])) {
+        atomic_init(&_value, defaultValue);
+    }
+    return self;
+}
+
+- (int32_t)value {
+    return atomic_load_explicit(&_value, memory_order_relaxed);
+}
+
+- (int32_t)increment {
+    int32_t old = atomic_fetch_add_explicit(&_value, 1, memory_order_relaxed);
+    return (old + 1);
+}
+
+- (int32_t)decrement {
+    int32_t old = atomic_fetch_sub_explicit(&_value, 1, memory_order_relaxed);
+    return (old - 1);
+}
+
+@end

+ 202 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m

@@ -0,0 +1,202 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#import <sys/mount.h>
+
+#import <CocoaLumberjack/DDFileLogger+Buffering.h>
+#import "../DDFileLogger+Internal.h"
+
+static const NSUInteger kDDDefaultBufferSize = 4096; // 4 kB, block f_bsize on iphone7
+static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone7
+
+// Reads attributes from base file system to determine buffer size.
+// see statfs in sys/mount.h for descriptions of f_iosize and f_bsize.
+// f_bsize == "default", and f_iosize == "max"
+static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
+    struct statfs *mountedFileSystems = NULL;
+    __auto_type count = getmntinfo(&mountedFileSystems, 0);
+
+    for (int i = 0; i < count; i++) {
+        __auto_type mounted = mountedFileSystems[i];
+        __auto_type name = mounted.f_mntonname;
+
+        // We can use 2 as max here, since any length > 1 will fail the if-statement.
+        if (strnlen(name, 2) == 1 && *name == '/') {
+            return max ? (NSUInteger)mounted.f_iosize : (NSUInteger)mounted.f_bsize;
+        }
+    }
+
+    return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
+}
+
+static NSUInteger DDGetMaxBufferSizeBytes(void) {
+    static NSUInteger maxBufferSize = 0;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        maxBufferSize = p_DDGetDefaultBufferSizeBytesMax(YES);
+    });
+    return maxBufferSize;
+}
+
+static NSUInteger DDGetDefaultBufferSizeBytes(void) {
+    static NSUInteger defaultBufferSize = 0;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        defaultBufferSize = p_DDGetDefaultBufferSizeBytesMax(NO);
+    });
+    return defaultBufferSize;
+}
+
+@interface DDBufferedProxy : NSProxy
+
+@property (nonatomic) DDFileLogger *fileLogger;
+@property (nonatomic) NSOutputStream *buffer;
+
+@property (nonatomic) NSUInteger maxBufferSizeBytes;
+@property (nonatomic) NSUInteger currentBufferSizeBytes;
+
+@end
+
+@implementation DDBufferedProxy
+
+- (instancetype)initWithFileLogger:(DDFileLogger *)fileLogger {
+    _fileLogger = fileLogger;
+    _maxBufferSizeBytes = DDGetDefaultBufferSizeBytes();
+    [self flushBuffer];
+
+    return self;
+}
+
+- (void)dealloc {
+    __auto_type block = ^{
+        [self lt_sendBufferedDataToFileLogger];
+        self.fileLogger = nil;
+    };
+
+    if ([self->_fileLogger isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        dispatch_sync(self->_fileLogger.loggerQueue, block);
+    }
+}
+
+#pragma mark - Buffering
+
+- (void)flushBuffer {
+    [_buffer close];
+    _buffer = [NSOutputStream outputStreamToMemory];
+    [_buffer open];
+    _currentBufferSizeBytes = 0;
+}
+
+- (void)lt_sendBufferedDataToFileLogger {
+    NSData *data = [_buffer propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+    [_fileLogger lt_logData:data];
+    [self flushBuffer];
+}
+
+#pragma mark - Logging
+
+- (void)logMessage:(DDLogMessage *)logMessage {
+    // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
+    __auto_type data = [_fileLogger lt_dataForMessage:logMessage];
+
+    if (data.length == 0) {
+        return;
+    }
+
+    [data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) {
+        __auto_type bytesLength = byteRange.length;
+#ifdef NS_BLOCK_ASSERTIONS
+        __unused
+#endif
+        __auto_type written = [_buffer write:bytes maxLength:bytesLength];
+        NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
+
+        _currentBufferSizeBytes += bytesLength;
+
+        if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
+            [self lt_sendBufferedDataToFileLogger];
+        }
+    }];
+}
+
+- (void)flush {
+    // This method is public.
+    // We need to execute the rolling on our logging thread/queue.
+
+    __auto_type block = ^{
+        @autoreleasepool {
+            [self lt_sendBufferedDataToFileLogger];
+            [self.fileLogger flush];
+        }
+    };
+
+    // The design of this method is taken from the DDAbstractLogger implementation.
+    // For extensive documentation please refer to the DDAbstractLogger implementation.
+
+    if ([self.fileLogger isOnInternalLoggerQueue]) {
+        block();
+    } else {
+        NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure");
+        dispatch_sync(DDLog.loggingQueue, ^{
+            dispatch_sync(self.fileLogger.loggerQueue, block);
+        });
+    }
+}
+
+#pragma mark - Properties
+
+- (void)setMaxBufferSizeBytes:(NSUInteger)newBufferSizeBytes {
+    _maxBufferSizeBytes = MIN(newBufferSizeBytes, DDGetMaxBufferSizeBytes());
+}
+
+#pragma mark - Wrapping
+
+- (DDFileLogger *)wrapWithBuffer {
+    return (DDFileLogger *)self;
+}
+
+- (DDFileLogger *)unwrapFromBuffer {
+    return (DDFileLogger *)self.fileLogger;
+}
+
+#pragma mark - NSProxy
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
+    return [self.fileLogger methodSignatureForSelector:sel];
+}
+
+- (BOOL)respondsToSelector:(SEL)aSelector {
+    return [self.fileLogger respondsToSelector:aSelector];
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation {
+    [invocation invokeWithTarget:self.fileLogger];
+}
+
+@end
+
+@implementation DDFileLogger (Buffering)
+
+- (instancetype)wrapWithBuffer {
+    return (DDFileLogger *)[[DDBufferedProxy alloc] initWithFileLogger:self];
+}
+
+- (instancetype)unwrapFromBuffer {
+    return self;
+}
+
+@end

+ 110 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m

@@ -0,0 +1,110 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+#if !__has_feature(objc_arc)
+#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
+#endif
+
+#import <CocoaLumberjack/DDMultiFormatter.h>
+
+@interface DDMultiFormatter () {
+    dispatch_queue_t _queue;
+    NSMutableArray *_formatters;
+}
+
+- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message;
+
+@end
+
+
+@implementation DDMultiFormatter
+
+- (instancetype)init {
+    self = [super init];
+
+    if (self) {
+        _queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", DISPATCH_QUEUE_CONCURRENT);
+        _formatters = [NSMutableArray new];
+    }
+
+    return self;
+}
+
+#pragma mark Processing
+
+- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
+    __block __auto_type line = logMessage->_message;
+
+    dispatch_sync(_queue, ^{
+        for (id<DDLogFormatter> formatter in self->_formatters) {
+            __auto_type message = [self logMessageForLine:line originalMessage:logMessage];
+            line = [formatter formatLogMessage:message];
+
+            if (!line) {
+                break;
+            }
+        }
+    });
+
+    return line;
+}
+
+- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message {
+    DDLogMessage *newMessage = [message copy];
+    newMessage->_message = line;
+    return newMessage;
+}
+
+#pragma mark Formatters
+
+- (NSArray *)formatters {
+    __block NSArray *formatters;
+
+    dispatch_sync(_queue, ^{
+        formatters = [self->_formatters copy];
+    });
+
+    return formatters;
+}
+
+- (void)addFormatter:(id<DDLogFormatter>)formatter {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters addObject:formatter];
+    });
+}
+
+- (void)removeFormatter:(id<DDLogFormatter>)formatter {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters removeObject:formatter];
+    });
+}
+
+- (void)removeAllFormatters {
+    dispatch_barrier_async(_queue, ^{
+        [self->_formatters removeAllObjects];
+    });
+}
+
+- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter {
+    __block BOOL hasFormatter;
+
+    dispatch_sync(_queue, ^{
+        hasFormatter = [self->_formatters containsObject:formatter];
+    });
+
+    return hasFormatter;
+}
+
+@end

+ 30 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/PrivacyInfo.xcprivacy

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTracking</key>
+	<false/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array>
+		<dict>
+			<key>NSPrivacyAccessedAPIType</key>
+			<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
+			<key>NSPrivacyAccessedAPITypeReasons</key>
+			<array>
+				<string>C617.1</string>
+				<string>0A2A.1</string>
+			</array>
+		</dict>
+		<dict>
+			<key>NSPrivacyAccessedAPIType</key>
+			<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
+			<key>NSPrivacyAccessedAPITypeReasons</key>
+			<array>
+				<string>E174.1</string>
+			</array>
+		</dict>
+	</array>
+</dict>
+</plist>

+ 104 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h

@@ -0,0 +1,104 @@
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2010-2024, Deusty, LLC
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms,
+// with or without modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+//
+// * Neither the name of Deusty nor the names of its contributors may be used
+//   to endorse or promote products derived from this software without specific
+//   prior written permission of Deusty, LLC.
+
+/**
+ * Welcome to CocoaLumberjack!
+ *
+ * The project page has a wealth of documentation if you have any questions.
+ * https://github.com/CocoaLumberjack/CocoaLumberjack
+ *
+ * If you're new to the project you may wish to read "Getting Started" at:
+ * Documentation/GettingStarted.md
+ *
+ * Otherwise, here is a quick refresher.
+ * There are three steps to using the macros:
+ *
+ * Step 1:
+ * Import the header in your implementation or prefix file:
+ *
+ * #import <CocoaLumberjack/CocoaLumberjack.h>
+ *
+ * Step 2:
+ * Define your logging level in your implementation file:
+ *
+ * // Log levels: off, error, warn, info, verbose
+ * static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
+ *
+ * Step 2 [3rd party frameworks]:
+ *
+ * Define your LOG_LEVEL_DEF to a different variable/function than ddLogLevel:
+ *
+ * // #undef LOG_LEVEL_DEF // Undefine first only if needed
+ * #define LOG_LEVEL_DEF myLibLogLevel
+ *
+ * Define your logging level in your implementation file:
+ *
+ * // Log levels: off, error, warn, info, verbose
+ * static const DDLogLevel myLibLogLevel = DDLogLevelVerbose;
+ *
+ * Step 3:
+ * Replace your NSLog statements with DDLog statements according to the severity of the message.
+ *
+ * NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!");
+ *
+ * DDLog works exactly the same as NSLog.
+ * This means you can pass it multiple variables just like NSLog.
+ **/
+
+#import <Foundation/Foundation.h>
+
+//! Project version number for CocoaLumberjack.
+FOUNDATION_EXPORT double CocoaLumberjackVersionNumber;
+
+//! Project version string for CocoaLumberjack.
+FOUNDATION_EXPORT const unsigned char CocoaLumberjackVersionString[];
+
+// Disable legacy macros
+#ifndef DD_LEGACY_MACROS
+    #define DD_LEGACY_MACROS 0
+#endif
+
+// Core
+#import <CocoaLumberjack/DDLog.h>
+
+// Main macros
+#import <CocoaLumberjack/DDLogMacros.h>
+#import <CocoaLumberjack/DDAssertMacros.h>
+
+// Capture ASL
+#import <CocoaLumberjack/DDASLLogCapture.h>
+
+// Loggers
+#import <CocoaLumberjack/DDLoggerNames.h>
+
+#import <CocoaLumberjack/DDTTYLogger.h>
+#import <CocoaLumberjack/DDASLLogger.h>
+#import <CocoaLumberjack/DDFileLogger.h>
+#import <CocoaLumberjack/DDOSLogger.h>
+
+// Extensions
+#import <CocoaLumberjack/DDContextFilterLogFormatter.h>
+#import <CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h>
+#import <CocoaLumberjack/DDDispatchQueueLogFormatter.h>
+#import <CocoaLumberjack/DDMultiFormatter.h>
+#import <CocoaLumberjack/DDFileLogger+Buffering.h>
+
+// CLI
+#import <CocoaLumberjack/CLIColor.h>
+
+// etc
+#import <CocoaLumberjack/DDAbstractDatabaseLogger.h>
+#import <CocoaLumberjack/DDLog+LOGV.h>
+#import <CocoaLumberjack/DDLegacyMacros.h>

+ 0 - 0
KulexiuForTeacher/Pods/CocoaLumberjack/Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h


Some files were not shown because too many files changed in this diff