普通视图

发现新文章,点击刷新页面。
昨天以前樵夫的小站

只是组件 使用手册

作者 Roy
2020年11月24日 21:07

本文为IOS软件“只是组件”的使用手册。从简单的如何添加组件到像PS一样编辑你自定义的组件,都可以在这里找到帮助。

本文由于内容较多,建议在导航中找到你需要的章节转跳,除了文章开头的图片,其余图片都做了隐藏。

软件介绍

只是组件是一款好看又实用的桌面组件工具,自从2020年10月上架以来,完全的自定义、便捷的设置和方便的转跳受到用户的广泛好评。使用只是组件,你可以拥有10+预设组件,包含相框照片、待办、日期记录等;你可以可视化自定义一个全新的完全不同的组件或者导入他人的组件;你可以将网络数据解析后显示在组件内,点击组件你设定的地方转跳执行特定操作。不仅如此,只是组件所有功能均可以免费使用,仅限制最多已添加组件数量。

image-demo

目前文档在更新中,预计十二月三十一号会完成更新。

希望你喜欢 只是组件!

基础内容

将组件添加到屏幕上

苹果官方给出了一个如何添加小组件的说明:见这里。

这里只需要选择“只是组件”,添加本软件的Widget即可。

如果以添加一个文字Widget为例,分为这几部:

  1. 进入软件主界面,点击“组件列表”,选择文字组件,点击右上角保存
  2. 按照官方添加小组件的方法,添加一个“只是组件”的小组件
  3. 长按“只是组件”的小组件,在弹出的菜单中选择“编辑小组件”
  4. 选择刚添加的文字组件

通过二维码分享组件

软件的所有组件都可以进行导入和导出,包括预设组件和自定义组件。

导入组件有两个入口,一个是桌面软件图标的Force touch,一个是主界面右上角的二维码图标。

导出组件在组件编辑界面右上角,图标为分享按钮。

导入导出都可以选择通过二维码或者通过字符串,如果组件包含的信息量过大,建议通过字符串导入导出。

这里提供一个导入导出简单演示的动图:点这里

使用激活码激活高级版

目前软件赠送的激活码均为单设备永久激活,激活码区分大小写。

激活方式为软件内激活。点击下方关于转到关于页,点击“分享应用”,在“激活码”框中输入。

如果激活码通过验证,稍等片刻软件就将解锁高级版内容。

如果激活失败,请确认软件是否有联网权限以及激活码是否准确没有缺失。

高级内容

创建自定义组件

软件主程在安卓时代曾经花了很多的时间在安卓自定义组件上,可惜苹果上没有那么便捷的自定义,十分遗憾。

所以有了这个类PS的完全自定义组件,可以从头做一个专属组件,也可以导入别人做好的组件。

不过,最首先的一点,在做自定义组件的时候记得随时保存,即点击右上角的对勾。

这里提供一个动图,简单的可以感受一下这个自定义组件的大致内容:这里

如果你愿意花几分钟看一下视频,可以点这里查看一个详细的操作演示:这里

当然,百闻不如一试,充分的自定义只等你来实现想法。

我们也会定期收集优秀的自定义组件,做一个展示和分享,在这里你可以找到许多好看的组件。

欢迎你分享你的优秀作品,可以通过二维码添加交流群或者微博@我

快捷转跳

Widget的一大实用功能就是快捷转跳,通过点击Widget的特定位置,可以打开微信扫一扫、微信支付、健康码、乘车码等等,甚至可以通过捷径实现一系列的自动化操作。

在软件中可以通过预设组件的快捷启动组件和自定义组件的转跳特效完成这一效果。

预设组件的快捷启动较为简单,这里就不多加赘述。自定义组件的添加转跳可以查看这个动图

关于快捷启动应该填写的内容,详细的可以查看这篇文章,软件未来可能也会增加一键添加捷径的功能。

获取网络数据

Widget很大的一个实用性限制就是只能显示一些本地的内容,如果可以便捷的显示任意网络内容,那就实用的多。

所以软件提供了任意网络数据的格式化与显示,你可以通过以下三种格式解析网络数据:

  1. Json
  2. 正则表达式
  3. 各版本RSS

网络数据会根据设定的更新间隔自动后台更新,但后台更新需要注意:

  1. 如果开启了省电模式将停用所有后台更新
  2. 苹果对于软件后台更新的策略是:系统统一安排空闲时间更新,不保证特定时间一定唤起更新
  3. 使用软件次数越多,系统提供后台唤醒的可能越多
  4. 使用快捷转跳后软件会进行必要的数据更新

所以为了保证软件表现正常,请尽量在Widget中使用快捷转跳,否则Widget将被认为是不常用软件,无法后台唤起。

具体的操作我们提供了一个使用演示:这里

其中文本解析中的规则,以数据源名字weather为例,如果数据内容是这样的:

1
2
3
4
5
6
{
"code": 0,
"data": {
"temp": 20,
}
}

那么获取数据中温度的规则就是:

1
2
$(fetch.weather.data.temp)
更新时间:$(fetch.weather)

其余的也是类似的规则,类推可知。

文本解析规则

软件的另一大特色功能(另一大文档大头)是一个文本解析系统,通过类似Excel的简单规则,拼接各种数据,数据我们需要的内容。

我们提供了一个操作的展示:这里

例如一个最简单的时间,规则就是:

1
$(date.HH:mm)

在网络数据一节还看到了关于网络数据的解析规则,除此以外,还有一些系统数据的获取规则。

类型关键词内容示例
电池batterylevel, state, charging, lowpower$(battery.level)
蓝牙bluetooth$(bluetooth)
系统recordcountry, language, reboot, brightness, volume, timezone, name, systemName, systemVersion, model$(record.brightness)
内存memoryall, active, activepercentage, inactive, inactivepercentage$(record.active)
硬盘diskall, active, activepercentage, inactive, inactivepercentage$(disk.active)
Wifiwifiip, ssid$(wifi.ssid)
健康healthstepCount$(health.stepCount)

之后将更新更多数据,文档也将同步更新。

FAQ

Q: 邀请好友后没有马上更新数据。

A: 邀请数据的统计会在每天十二点更新,所以第二天再看就有新的数据了。

Q: 组件有时候会卡住不更新数据。

A: 组件本身刷新有非常小的概率出现这个问题,目前已经尽量降低刷新频率避免发生这个问题。如果的确卡住了,点进去重新保存一下即可。

Q: 后台数据没有按照更新时间准确更新?

A: 首先,请查看是否打开了软件的背景数据刷新,在设置中搜索”只是组件“。背景数据刷新本身不支持准确的时间间隔,系统会根据软件的要求协调各个软件在必要的时候刷新,但总体而言软件使用频率越高,系统会越多分配背景数据的刷新。只是组件支持了各种唤醒方式,尽量保证了刷新。建议配合快捷启动使用软件,使用快捷启动可以保证数据的按时刷新。

IOS常用URL Scheme整理

作者 Roy
2020年10月12日 12:19

Anchor在Mac版本之后,终于IOS版终于在十一也过审了。IOS版的主要功能就是快捷启动和好看的各种显示,其中快捷启动就是URL Scheme的功能,所以就有了这篇文章,能给你们的使用带来一些便利。

写在前面

关于软件

如果你还没有用过Anchor,我强烈建议你下载,App Store即可免费获取。快捷启动和高度自定义组件合集,这里是软件链接

关于URL Scheme

URL Scheme是每个程序内置的快捷启动,并不一定有特定的格式,是存在一定可能随着某次更新产生更改的。

通过访问该链接,可以转跳相应的软件的相关功能。

部分软件,例如捷径,本身保证URL Scheme有特定的格式。

部分软件,例如微信,不保证URL Scheme内容和格式的稳定,在某次更新后目前只有扫码链接可用,非常糟糕,是常用软件里支持最少的。

这种快捷启动会给日常使用带来大量便利,例如支付宝的扫码、付款码、收款码,各个设置页面等等。

而在Anchor的设置中,每个组件都可以设置URL Scheme产生点击后的后续操作。只需要将下述的字符串输入即可。

系统

用途URL Scheme
钱包shoebox://
邮件mailto://anchorapp@126.com
电话tel://12345678
短信sms://12345678
显示日历calshow://
照片photos-redirect://
设置prefs://

支付宝

用途URL Scheme
扫一扫alipayqr://platformapi/startapp?saId=10000007
打开收款alipays://platformapi/startapp?appId=20000123
乘车码alipayqr://platformapi/startapp?saId=200011235
付款码alipay://platformapi/startapp?appId=20000056
快递查询alipays://platformapi/startapp?appId=20000754
健康码alipays://platformapi/startapp?appId=20000067&url=https%3A%2F%2F68687564.h5app.alipay.com%2Fwww%2Findex.html

微信

用途URL Scheme
扫一扫weixin://scanqrcode

即刻

用途URL Scheme
搜索jike://page.jk/search
用户jike://page.jk/user/6b8159a9-bd2e-435d-ab9d-a8acba6778b0
主题jike://page.jk/topic/[topic-id]
消息jike://page.jk/message/[message-id]

其他软件

用途URL Scheme
微博sinaweibo://
淘宝taobao://
QQ音乐qqmusic://
网易音乐听歌识曲orpheuswidget://recognize
Bilibilibilibili://

捷径

用途URL Scheme
调用捷径shortcuts://run-shortcut?name=[name]&input=[input]
创建捷径shortcuts://create-shortcut
打开特定捷径shortcuts://open-shortcut?name=[name]

调用捷径后,在捷径中可以完成的内容就非常多了。

MacOS在NSImage里提供了哪些系统图标

作者 Roy
2020年7月22日 09:01

近两周闲下来的时间都在写一个新的App,都没时间写字和写文章。昨天美工大哥问了我一个问题,MacOS到底有哪些系统图标可用?图标文件夹的确是一个解决方案,但保证可用还是得额外加进项目的素材库,和外加的图标没有区别。真的内置的图标的话,这个问题换句话说是NSImage.Name里面到底有哪些图标。本文用看着也越来越重要的SwiftUI,抓取开发者文档的数据展示可用的图标和尺寸。

已有资料

开发者文档里有专门的一页,列出了目前可用的系统图标。但只有名字,没有相应的图片放在边上,也看不到尺寸。

github上发现了一个项目,可惜几年不更新了,图标数据也是写死在代码里的。项目名字叫fucking_nsimage_syntax,不知道是不是也在吐槽开发者文档里不放图和尺寸。

如果自己写一个的话,基本是,网页抓取数据->数据对应图标和尺寸->SwiftUI展示,也很简单。

数据抓取

开发者文档载入是先框架后数据,简单看一下包,可以找到图标页的数据通过获取这一个网址取得:

1
https://developer.apple.com/tutorials/data/documentation/appkit/nsimage/name.json

既然已经想好SwiftUI展示,那就直接做进界面的方法里好了,这里就是一个简单的网页请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension ImageNameDemoView {
func getImages(callback: @escaping ([String]) -> Void) {
let url = URL(string: "https://developer.apple.com/tutorials/data/documentation/appkit/nsimage/name.json")!
let task = URLSession.shared.dataTask(with: url) { d, _, _ in
guard let d = d else { return }
if let r = try? JSONSerialization.jsonObject(with: d, options: .mutableContainers) {
if let refs = (r as! [String: Any])["references"] {
var rl = [String]()
for v in (refs as! [String: Any]).values {
let n = (v as! [String: Any])["title"] as! String
if n.hasSuffix("Name") {
rl.append(n)
}
}
callback(rl)
}
}
}
task.resume()
}
}

我们要的数据全都在references键里,通过判断后缀是否有Name去除非图标的内容。

数据对应图标和尺寸

这里获取了需要的数据的数组,但这个数据只是属性名不是实际的值,所以还需要一个转化。

理论上来说这是个很简单的过程,想象中通过value(forKey:)就可以解决。但NSImage现在还不是正常实现的类,所以这个方法和同理的反射都不能用。

这里是可以OC去取值的,但实际上取巧的办法就可行:

1
2
3
4
5
6
7
extension ImageNameDemoView {
func getNSImageName(_ s: String) -> String {
var r = "NS\(s.prefix(1).capitalized)\(s.suffix(s.count - 1).prefix(s.count - 5))"
if r == NSImage.applicationIconName { r = "" }
return r
}
}

图标和对应的尺寸就简单了:

1
2
NSImage(named: name)
NSImage(named: name)?.size.debugDescription ?? ""

SwiftUI展示

大致构思一下界面,这里一行放十个图标,放一个按钮加载数据(onAppear也成),右击图标显示属性名和大小。

所以大致的框架就出来了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct ImageNameDemoView: View {

var columnNumber = 10
@State var images: [String] = []

var body: some View {
VStack {
ForEach(0 ..< images.count / columnNumber + 1, id: \.self) { y in
HStack(spacing: 5) {
ForEach(0 ..< self.columnNumber, id: \.self) { x in
VStack(spacing: 5) {
if x + y * self.columnNumber < self.images.count {
// replace Text with Image
Text("\(self.images[x + y * self.columnNumber])")
}
}.frame(width: 30, height: 30)
}
}
}
Button("Press") {
self.getImages() { l in
self.images = l
}
}
}
}
}

下面把中间的文字换成有右键菜单的图片:

1
2
3
4
5
6
Image(nsImage: NSImage(named: self.getNSImageName(self.images[x + y * self.columnNumber])) ?? NSImage())
.contextMenu {
Text("\(self.images[x + y * self.columnNumber])")
.font(.system(size: 16))
Text("\(NSImage(named: self.getNSImageName(self.images[x + y * self.columnNumber]))?.size.debugDescription ?? "")")
}

界面就快速的完成了,大概是一个这样的界面:

demo-image

其他

SwiftUI已经有了越来越好的开发体验,单数据源绑定的逻辑,完备的组件和方法,让界面的构建变得十分简单。

加上Widgets等内容的大力支持,地位可以想见会快速上升。

就我的体验而言,目前有一些问题还是需要有思想准备。Swiftui错误提示仍旧非常糟糕,界面中小的语法错误会导致整个构建时间耗时非常久,并最后返回,最外层View无法推断返回值类型这样的无用信息。

可能搜出来的答案很多是不合适的用法,也许我是个例,也许OC程序员写Swift都一样,看自动补全有好像能用的方法用了就行。例如文本框获取焦点,搜到的都是view.becomeFirstResponder()。IOS上奇怪的可用,MacOS上奇怪的不能用。翻翻文档才会发现调用view.window?.makeFirstResponder(view)才是标准的用法。

以及一些小问题,即使有NSViewRepresentable的万能后备,有时候还是会因为一些神奇的原因重写整个组件,例如目前List在MacOS上如果需要去掉分割线的背景需要重写整个List,甚至没有IOS上取巧的直接改写UITableView的办法。

但总体,惊喜是会比坑多的,建议一试。

浅析Pandownload作者可能面临的责任

作者 Roy
2020年4月18日 18:31

本文不涉及百度的企业社会责任,只聊一聊软件作者可能涉及的刑事、民事责任,提供多一个角度。2020年4月初,扬州宝应网安对Pandownload的作者进行了刑事上的抓捕,而不是类似案件中常见的民事申请诉前证据保全。一来,这从法律上来说不可谓不是一个创新。二来,由于没有开源,本来想的从代码角度写cherryljr做了什么就变成了不应该干的事情。因为这两个理由我写了这篇文章,多一些了解也是多一些保护。

背景

扬州网警巡查执法通过新浪微博发布消息称四月初抓捕了Pandownload的作者,舆论发酵后删除,凤凰网对原微博进行了截图

今年 2 月,受害人刘某报案称其下载的“Pandownload”软件会在未授权的情况下,将自己百度网盘的数据共享出去,导致隐私照片和文件泄露。宝应县局网安民警立即开展案情分析研判,研习法律条文,解剖软件结构。面对满屏的数据,一盯就是几个小时。

经过梳理,警方发现该软件可以以非会员权限突破百度网盘官方设定,实现高速下载,系侵入、非法控制计算机信息系统的程序、工具,并且该软件的使用者达到数万人,致使北京百度网讯科技有限公司(“百度公司”)损失高达上千万元。

4月初,在掌握了充分证据后,宝应网安远赴广东,抓捕犯罪嫌疑人蔡某萌,当场查获电脑、手机等作案工具。

可以看出,该案已经作为刑事案件被立案侦查。目前,该项目与之后暂时出现的替代项目均关闭。

刑事责任

根据微博的内容,“该软件可以以非会员权限突破百度网盘官方设定,实现高速下载,系侵入、非法控制计算机信息系统的程序、工具”,即触犯非法获取计算机信息系统数据罪

不用讨论的是,若cherryljr的软件存在后门窃取用户数据或者破解手段为入侵百度服务器,则显然构成犯罪。有意思的是,曾经有一个收费提供百度云盘资源搜索的软件,法院判定软件上传使用者分享链接的行为构成窃取用户数据,判决见这里。这次的案件报案人也是采取该理由,但盘搜技术上需要上传分享链接用于搜索,而破解限速仅下载即可。所以除非cherryljr存在后门行为,不会因为报案人的理由构成犯罪。

值得讨论的是,若仅是截取了下载链接,通过三方工具下载,是否仍旧会构成犯罪。虽然涉案软件没有开源,但类似有很多项目,其实都是这样的操作。我们就假设,截取链接三方下载,是比较接近涉案软件的解决方案。

刑法条文

根据我国刑法第285条一、二款:

【非法侵入计算机信息系统罪】违反国家规定,侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统的,处三年以下有期徒刑或者拘役。

【非法获取计算机信息系统数据、非法控制计算机信息系统罪】违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

侵犯法益

该罪保护的是社会法益,而非企业法益。非法获取计算机信息系统数据罪处于刑法第六章妨害社会管理秩序罪的第一节扰乱公共秩序罪,刑法体系而言,整章的犯罪均规制的是侵害社会法益的行为,包括该罪。若仅是企业的权益受到了损害,理应通过民事赔偿完成救济。

司法解释中也重点强调了公众隐私的保护,公众个人信息数量较小的非法获取也构成犯罪。

即使过度使用免费通道的行为侵犯了财产性利益,也应该认定盗窃罪。

犯罪行为

这里的技术问题可以做个类比,网盘就类似一家面包店,面包收钱,而试吃小样免费。面包店限制了一天只能买一次,所以不掏钱买面包就只有小样,吃不饱。于是聪明的人就多注册几个账号,去薅羊毛,也吃饱了。类比中,截取下载链接就是找到了小样窗口,三方多线程工具下载就是多账号薅羊毛。

需要注意到一个区别,本案并没有侵犯不公开数据。窃取阿里云备案完整信息案和本案不同。完整的备案信息含有个人隐私等不公开内容是内部文件,即使由于操作失误公网可以访问,恶意获取也属于非法。而本案中百度本就对所有用户提供免费的下载,数据也由其他用户上传。

涉案免费通道的使用边界不明,通过技术手段跑满免费带宽应属于合理使用。由于采取了更好的下载技术,同一链接,使用普通下载器和aria2下载,下载速度完全不同。同样的,aria2相对于百度提供的客户端而言,可以在同一链接的情况下获得更好的下载速度,使用更好的下载手段不应被认定为滥用。免费通道由于该技术满足了用户的下载需求,的确会产生了不符合自身商业意图的结果,公司可以从技术上进行限制,例如限制IP、下载模式识别、更高精度的限速模型、更低的限速、甚至关闭免费下载,来鼓励用户完成付费。

网盘产品主要是提供虚拟的存储空间,付费用户更快下载他人文件是附加功能,并非主要卖点及付费点。网盘归根到底是提供了云端的存储空间,用户可以云端备份文件、节省本地空间、同步手机相册等。网盘并非内容提供方,网络资源共享的途径多样,若网盘获取速度过慢,完全可以通过其他方式共享。优化下载文件速度的行为在另一方面也鼓励企业、个人将文件通过该网盘存储,难以判断商业上由此造成的影响。

情节严重

根据刑法条文可知,实施犯罪行为的情况下需情节严重才构成犯罪,情节严重的具体解释规定在相关司法解释中。

具有下列情形之一的,应当认定为刑法第二百八十五条第二款规定的“情节严重”:

(一)获取支付结算、证券交易、期货交易等网络金融服务的身份认证信息十组以上的;

(二)获取第(一)项以外的身份认证信息五百组以上的;

(三)非法控制计算机信息系统二十台以上的;

(四)违法所得五千元以上或者造成经济损失一万元以上的;

(五)其他情节严重的情形。

本案可能涉及的为第四条,而本案的软件的确存在可能的经济损失。

违法所得更难证明,但也有更多可以讲的内容。性质而言,本案软件的获利方式为捐赠,但的确存在捐赠才可以获得最新测试版的情况,实践中可能被认为是一种特定的商业模式。金额而言,会存在是否扣除成本的两种观点,一般作为定罪处罚标准时会采取扣除成本的观点,对此人民法院报也有过一篇专门的报道

是否构成财产犯罪

如果认定对网盘免费带宽的使用非法,本案的确可能存在非法占有财产性利益的情况。这个情况下仍需要注意,本案盗窃罪成立原因为达到了数额较大的起点,情节轻微,退赃退赔可不作为犯罪处理。另外,就该案的情节而言,违法行为系违法使用带宽,社会危害性较小。

民事责任

最基本的思路肯定是侵权责任纠纷,违法使用带宽,造成了额外的带宽费用,额外的带宽费用由违法行为导致,侵权人显然存在过错。

所以还有一个思路是不正当竞争纠纷。一,由于网站知名度等因素也会被认定为利益,认定营利性服务的经营者没有问题。涉案软件与网盘客户端的功能而言,使用涉案软件显然会导致不再使用网盘客户端,双方存在竞争关系。二,涉案软件必然影响网盘正常经营,研发时显然可以预见到。三,网盘旨在通过提供有区别的存储、下载等服务鼓励用户付费。涉案软件通过免费高速下载,让用户失去了付费的理由,破坏了网盘的商业模式。

不过就金额而言,带宽的费用损失难以精确的计算,侵权人违法所得也大都是小额捐赠,数额不大。

结语

舆论一直在发酵,一方面,破解软件的作者受到了某种意义的保护,另一方面,百度网盘的会员也安全了,真是奇妙的双赢。

避免docker挂载时产生root权限文件

作者 Roy
2020年3月24日 10:03

在docker挂载磁盘的时候,由于很多容器内使用root运行程序,会导致挂载中产生的文件属于root:root。一般容器外用户并不是root,会让文件共享甚至阅读日志产生不必要的麻烦。本文旨在不更改容器的情况下,从根本上解决产生root权限文件的问题。

docker产生的文件经常需要sudo chmod o+rw *然后访问,虽然麻烦,但好歹有sudo的权限。没有sudo权限的时候就没那么走运了,这时候我发现了三种操作可以解决这个问题。

文章分为三部分,解释了挂载的原理、提供了三种解决原理、就方案进行演示。若仅希望获得推荐的解决方案,可以直接跳到方案演示的使用子用户一节。

文件挂载原理

简而言之,docker的文件挂载中,容器内外uid与gid相同

最简单的挂载磁盘,例如:docker run -v $PWD:/data bash,就是将当前目录挂载到容器内的/data目录。文件的权限可以通过ls -l查看。

容器内可能有使用root用户和非root用户运行程序的两种情况。1)若容器内使用root运行程序,可以想象,产生的文件属于0:0,也就是root:root。容器外文件也会属于0:0,同样是root:root。这样产生的文件就需要容器外的root权限才可以操作。2)若容器内使用非root运行程序,例如blog:x:1000:1000::/home/blog:/bin/bash,产生的文件属于1000:1000。容器外文件也会属于1000:1000,而容器外uid为1000的用户可能是frank,这是若碰巧你就是frank,你就可以顺利读取这个文件。但若你是uid为1001的david,而且frank是个讨厌鬼,你就告别这个文件了。甚至容器外根本没有uid为1000的用户,那这个文件就不属于任何人。

三种解决原理

限定开启用户

部分容器可以使用该方案,且难以判断是否可行。对于这些容易配置权限的容器,这个问题很容易解决,强制容器的运行用户docker run -u blog bash即可。

我们这么定义容易配置权限的容器。

  1. 容器内所有用户都可以访问容器内所有需要访问、运行的资源

  2. 容器运行时由运行用户生成所有文件

  3. 由运行用户访问挂载磁盘内需要访问、运行的资源

满足这些要求,会让配置一个稍复杂容器的工作变得复杂许多甚至不可完成。如果你创建过容器,那你一定可以想象。第一点起码可以通过批量更改所有文件的o属性完成。第二点和第三点,容器运行的程序若需要绑定保留端口,就需要特别的各种设置;容器运行的程序若简单的要求需要管理员权限,我们不可能为了创建符合要求的docker更改甚至反编译程序。

而我们大部分时间使用到的容器都是他人配置好的,我们无法判断他人配置的容器是否属于容易配置权限的容器。这种容器若强制容器的运行用户除了无法保证产生文件的权限,还可能让容器意外崩溃。但说不准,bug就莫名奇妙消失了?

使用子用户

若容器内外文件的uid:gid可以不同,这个问题就可以很容易解决。的确有这么一个方案,让容器内所有的uid:gid以一定的规则映射,我推荐使用这一方案。文章的方案演示节中我会演示这一方案。

在官方文档中,这个方案本身是为了解决安全问题,见这里。发生容器挂载磁盘root权限提权也不是一次两次了,这算是一个官方的解决方案。

但这个方案会有一些限制

  • 不可使用--pid=host--network=host
  • 不可使用无法识别或使用用户映射的挂载磁盘
  • 若使用docker run --privileged则必须同时使用--userns=host

都是不大的限制,使用的时候留个心眼即可。

osxfs

有趣的是,OSX中不存在这一问题。

在和朋友讨论的时候,朋友们纷纷反映没有这个问题,不禁让我感叹贫穷限制了我的想象力。如果你使用的是OSX或者和我一样从朋友那里抢来了一台OSX,你可以尝试如下命令:

1
2
3
4
5
6
7
8
mkdir data
docker run -it --rm \
-v $PWD/data:/data \
bash:5.0 \
touch /data/testfile
ls -l data

# -rw-r--r-- 1 user staff 0 0 0 0:00 testfile

尽管OSX版本的docker有各种问题,但起码没有这个问题。这一切要归功于osxfs,官方文档中有一个基本的介绍

其中Ownership一段提到了解决方案,容器内root映射到容器外运行docker的用户,容器内更改文件所有人的记录保存在com.docker.owner中。具体细节还有一些别的,TL;DR。

Initially, any containerized process that requests ownership metadata of an object is told that its uid and gid own the object. When any containerized process changes the ownership of a shared file system object, such as by using the chown command, the new ownership information is persisted in the com.docker.owner extended attribute of the object. Subsequent requests for ownership metadata return the previously set values. Ownership-based permissions are only enforced at the macOS file system level with all accessing processes behaving as the user running Docker. If the user does not have permission to read extended attributes on an object (such as when that object’s permissions are 0000), osxfs attempts to add an access control list (ACL) entry that allows the user to read and write extended attributes. If this attempt fails, the object appears to be owned by the process accessing it until the extended attribute is readable again.

所以内容被存储到了这里:

1
2
3
4
5
6
7
8
docker run -it --rm \
-v $PWD/data:/data \
bash:5.0 \
chown nobody /data/testfile
ls -l@ data

# -rw-r--r--@ 1 user staff 0 0 0 0:00 testfile
# com.docker.owner9

所以这个方案无法应用在其他系统,而一般OSX上的docker也就是个测试。

一些其他的想法

如果使用特定用户构建容器,最后也是这个用户使用容器,就没有这个问题了。比如构建时容器内用户uid为1000,使用容器的用户uid也为1000。不过这样的容器基本不可移植,但自用时也不失为一个简单的办法。

另一个想法是,是否有用户的权限低于所有用户。就像root用户可以访问所有用户文件类似,若有一个用户的文件可以被所有用户访问,那使用这个用户创建docker就解决了这个问题。但实际上找不到这样一个用户,即使是nobody也不行,只有没有人可以访问没有人的文件。

方案演示

限定开启用户

容器内强制由容器外的当前用户运行容器内的程序。

1
2
3
4
5
docker run -it --rm \
-u `id -u` \
-v $PWD/data:/data \
bash:5.0 \
touch /data/solution1

可以看到生成的文件由当前用户所有。

1
2
3
ls -l data

# -rw-r--r-- 1 blog blog 0 0 00:00 solution1

该方案最为简单,但正如原理节讲到的,对于很多容器,强制容器的运行用户无法保证产生文件的权限,还可能让容器意外崩溃。

使用子用户

该方案需要更改docker守护程序的运行参数,测试的时候建议开个虚拟机,这里以一个未进行任何配置的Ubuntu 16为例进行展示。

先完成准备,安装docker并创建用户blog作为演示用户。

1
2
3
4
5
6
7
8
9
10
# install docker
curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh

# add user blog
useradd -d /home/blog -m -s /bin/bash -G sudo,docker blog
passwd blog

# switch to blog
su blog
cd ~ && mkdir data

若不设置子用户,生成的文件将会归root所有。

1
2
3
4
5
6
7
docker run -it --rm \
-v $PWD/data:/data \
bash:5.0 \
touch /data/blog_without_subuid

ls -l data
# -rw-r--r-- 1 root root 0 0 00:00 blog_without_subuid

subuid的设置被放在/etc/subuid中,当前用户的uid可以通过id -u获取。subgid的设置被放在/etc/subgid中,当前用户的专属group可以通过cat /etc/group | grep $USER获取。这里假设blog:blog对应的uid:gid为1000:1000,那么这两个文件按照如下进行设置。子用户的格式是name:start:number,意思是名字为name的用户生成的子用户是从start开始的number个。这两行产生了容器外uid为1000, 100000, 100001, 100002...的一系列用户,和容器内的用户一一对应,容器内的uid为0, 1, 2, 3...。显而易见,容器内的root映射到了容器外的当前用户,子用户组也是同理。

1
2
3
4
5
6
7
# sudo vim /etc/subuid
blog:1000:1
blog:100000:65536

# sudo vim /etc/subgid
blog:1000:1
blog:100000:65536

下面通过设置docker的配置文件,让docker知道需要启动用户映射,然后重启docker即可。

1
2
3
4
5
6
# sudo vim /etc/docker/daemon.json
{
"userns-remap": "bloger"
}

# sudo service docker restart

到这一步,子用户的设置就完成了,可以试一试效果。

1
2
3
4
5
6
7
docker run -it --rm \
-v $PWD/data:/data \
bash:5.0 \
touch /data/blog_with_subuid

ls -l data
# -rw-r--r-- 1 blog blog 0 0 00:00 blog_with_subuid

虽然容器内使用root生成了归属于root的文件,但是容器外文件归属于设置了映射的当前用户。

一些有关幽默的理论

作者 Roy
2020年2月10日 23:49

近期隔离在家,靠段子手获得了大量的快乐。一直在想这样一个问题,搞笑是一个幽默的人说了一件事,还是一个人说了一件幽默的事。之前看王自健的相关新闻,又一次看到对李诞的评论是,他是可以坐下来一页一页出段子的人,真是让人羡慕的能力。本文检索了一些幽默理论,结合脱口秀大会第二季呼兰决赛的稿子尝试一下分析和归类。

西方幽默理论

西方幽默理论有三个主要的理论流派,优越论、释放论和乖讹论。

优越论

这个理论认为,幽默是对某人或者某物的嘲笑,并由此获得优越感。这种方式包括表演者实施嘲笑,也包括表演者充当被嘲笑的对象。

萧伯纳一次在舞会上邀请一位大龄妇女,大龄妇女十分激动,询问自己是有什么过人之处。萧伯纳回应道,“我请你跳舞是因为这是一个慈善舞会,不是吗?”

仔细看来,甚至这个笑话可以说有侮辱妇女的成分,但的确是个好例子。

比如最近很火的小破站MV,自夸小队。其中其余三人顺利戴上了眼镜,中国boy不小心戳到眼睛。

比如卓别林、憨豆、周星驰,很多搞笑通过扮演被嘲笑的对象来达成。

释放论

这个理论认为,幽默是出于有压力的感情解除的确认。用弗洛伊德在《论幽默》一问里面的话说,就是感情消耗的节约。

金马奖上颁奖嘉宾调侃黄渤:你怎么穿个睡衣就来出席颁奖礼?

黄渤:因为这五年一直都在金马奖现场,这里已经像家一样。回到家里穿什么?回到家里一定要穿得舒服一点儿。

如果移除金马奖颁奖的紧张氛围,只是私下轻松的氛围下进行这段对话,就很难感受到搞笑。

乖讹论

这个理论认为,幽默是合理的期待落空。就是听众在笑话过程中产生了预期的结果,但结尾时获得了期待以外的结果,而这个意料之外的结果又异常和谐。

A: 你说我如果戴上分院帽会被分到哪个学院?以我的聪明应该会是斯莱特林吧。

B: 阿兹卡班。

这一理论特别常见,常见的抖机灵、搞笑配图、神最右大多是这一理论。

在文字横线处补写恰当的语句,使整段文字语义完整连贯。每处不超过15个字。

在为玫瑰剪枝的时候,不小心被刺刺到,一滴血珠渗出拇指,鲜红的血,颜色和盛放的红玫瑰一模一样。___?我在心里疑惑着。我一边吸着手指渗出的血珠,一边想着,……

答案:我缓缓打出一个问号

这需要大量的联想和创意,甚至铺垫两条故事线,好让最后的答案意料之外又情理之中。

例如思文奶奶的故事

举例分析

以下是脱口秀大会第二季呼兰决赛的稿子,不用冠军卡姆的稿子主要是因为卡姆的风格偏向于表演,很大部分的内容不在文字上,呼兰的脱口秀就更传统。

比赛到现在真的是弹尽粮绝,比赛三天前我的稿子还一塌糊涂。傍晚的时候给我爸发微信,我说,爸,我真写不出来,把我写的乱七八糟的发给他了。我爸他都没说安慰我一下,他回了我一下,我在外面应酬,等我回去我给你写一篇。我都没当回事儿,晚上十一点多,我爸发了一篇两千多字的稿子过来。我说哇,我在节目里面这么吐槽我爸妈,他还给我写稿子,这难道就是亲情的力量吗?结果一看那稿子,是我爸吐槽我爷爷的。

前面的铺垫产生了父子亲情的逾期,以为是个温暖的故事,没想到结尾是个一脉相承的吐槽父亲。另一个层面上来说,本来亲情和对儿子的支持是一个沉重的话题,最后的转折让紧张的气氛有了一个释放。

是吧,写的不咋地。好的话我就都用了嘛,我又不傻。

自己套入图方便、拿来主义的人设,可以算是一种自我贬低以产生搞笑的效果。

我爸写的脱口秀甚至有讽刺意味,可能是看了我的脱口秀,你这脱口秀怎么和你奶奶的鸡蛋酱似的,写脱口秀不舍得放梗呢。你这个脱口秀整麻烦了,找本字典里面放一个梗,天天能说脱口秀。

甚至用了夸张的手法,讽刺了自己脱口秀梗少的情况。

我小时候在东北,我爸有一次来一个英国的客户,随便吃了两顿饭之后英国人就飘了,觉得中国这酒也不好喝,中国人也不能喝,我这两天都没喝多。我爸想,这么合理的诉求,应该得到满足。就去跟他喝,临行前还问我说,就咱东北那些喝酒的绕口令,感情深一口闷,感情浅舔一舔,感情厚喝不够,感情铁喝吐血。用英语咋说呀?

最后一句用英语咋说呀,是意料之外不会去想到直接翻译的,但又很符合老父亲人设。

扩展阅读

1、弗洛伊德 《诙谐及其与无意识的关系》

2、格雷格·迪安 《手把手教你玩脱口秀》

3、《脱口秀大会》

手绘一个苹果手表的动图表盘

作者 Roy
2019年10月13日 16:31

三个月前,即刻还没改名Jellow,打开即刻还能刷刷刷的时候,我看到过一张别人的苹果手表表盘。但是我尝试了各种关键词,翻了各种常见不常见的苹果动图表盘网站、微信,以图搜图,都没有找到。所以最后的解决方法就是手绘了,这篇文章就是整个手绘动图生成动态表盘的过程。具体内容是手绘素材、Photoshop生成动图、intoLive转化为live photo。

在文章的最开始,我把效果图先展示一下,这就是我对着找了好久找不到的原图。自己做一个gif还挺有意思的,所以我把这部分也留了下来。文章中需要的最终成品gif可以在这里右击另存为下载,只想看设置的可以下载好直接跳到“转化为live photo”一节。

效果图

基础思路

肯定不是所有人都了解怎么设置苹果手表的自定义动图表盘,这其实是一件很简单的事情。

苹果有一个拍摄live photo的功能,就是一张照片在后台还会同时存储一个mov格式的视频,当你在相册里长按图片就会播放这个视频,还原拍摄前后的情况。而如果把这种live photo设置成手表表盘,手表亮起时会自动播放该图片。

所以基础的思路就有了,制作动图,转化成live photo,设置到表盘。

制作动图

处理图片就得说一句Adobe大法好。

就素材这一块,数位板、Inklet或者甚至备忘录都行,为了做这张动图,需要以下这一串图片:

歪头鸭素材

之后我们需要把这些图片每一个作为一个图层导入Photoshop,通过开关可视达到gif的效果。

窗口中有一个时间轴需要打开,之后在时间轴里新建帧,开关可视即可。

每一帧的时间也可以不设置,反正intoLive会把每一帧都改成0.1秒。

转化成live photo

原理来说,只要给图片添加mov就可以制作出gif,但这是苹果系统。

我试了一下能不能写个小脚本直接转,不过这个操作实在没办法脚本完成。写一个ios应用是可以实现的,不是很难,不过这和装个软件也没啥差别了。如果你感兴趣可以看一下这篇文章,里面具体讲了怎么手动制作live photo。

我们这里采用了intoLive,这个软件在保存图片时有个广告,可以接受啦。

读取图片,点击右上角制作,保存到相簿,三步就完成了。

设置到表盘

设置到表盘之前我们需要让这张live photo最后停在冒着问号的界面,这就需要设置一下live photo的主要照片。

在相册里找到导出来的live photo,点击下面最左边live photo的标识,再把大图下面进度条拖到最后,点击设为主要图片,保存即可。

最后就是把live photo设置到表盘,点击分享(我的ios版本里是在左下角),找到一个创建表盘。

之后在苹果手表上设置使用该表盘就可以了。

最后有一个小提示,如果你是常亮版本的手表,关闭常亮就可以看到效果了。

用markdown编写man文档

作者 Roy
2019年9月9日 21:09

晚上有些碎片时间可以用,想着接一接CMPP的man翻译。一直很好奇man文档都是怎么编写出来的,背后是什么格式的文本,于是就接触到了roff格式。这篇文章介绍了三种文件格式,md、roff、富文本,就scpp作为例子展示了从md如何生成man文档并安装。如果你想要生成自己的man文档,或者想要了解这几个文档格式,不妨看一下这篇文章。

准备内容

本文中介绍三种文件格式的区别的时候,转换roff语言与富文本使用到了groff命令,可能需要安装。

在md转化为富文本的时候除了groff命令,还使用到了pandoc命令,可以进行一个安装

三种文档

以scpp的文档展示一下三种格式的区别。

富文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
SCPP(1)                                                                SCPP(1)



NNAAMMEE
scpp - scp plus

SSYYNNOOPPSSIISS
ssccpppp [ push | pull ] [ scp options ] source target

DDEESSCCRRIIPPTTIIOONN
ssccpppp simplifys file transformation between two machines by extending
basic command scp. scpp is a one-line command that added to your bash
profile. You need to specific username ,server address and options
before using.

OOppttiioonnss
ppuusshh Push files to remote machine.

ppuullll Pull files from remote machine.

ssoouurrccee Specify the source file.

ttaarrggeett Specify the target file.

SSEEEE AALLSSOO
scp

BBUUGGSS
Please report to minjie_gu@126.com.



9 September 2019 SCPP(1)

根据不同的终端,富文本的显示是通过加入不同的特殊字符达到效果。

由于是放在代码框里面,特殊字符不会被处理成加粗、倾斜等格式,这些在终端中会显示。

下文生成的scpp.1就是包含特殊字符的文档。

终端不同,特殊字符的约定不同。也就是说Windows终端上生成的富文本放到Linux的终端上显示就可能不正常。

由于书写麻烦也不通用,而源文档需要书写简单而且通用,所以有了下面两种格式。

roff格式

roff是gnu默认的标记语言,用纯文本就可以写出有格式的文档,大量我们man命令看到的内容都是用这个格式写成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.TH SCPP 1 "9 September 2019"
.SH NAME
scpp \- scp plus
.SH SYNOPSIS
\fBscpp\fP [ push | pull ] [ scp options ] source target
.SH DESCRIPTION
\fBscpp\fP simplifys file transformation between two machines
by extending basic command scp. scpp is a one-line command
that added to your bash profile. You need to specific
username ,server address and options before using.
.SS Options
.TP
\fBpush\fP
Push files to remote machine.
.TP
\fBpull\fP
Pull files from remote machine.
.TP
\fBsource\fP
Specify the source file.
.TP
\fBtarget\fP
Specify the target file.
.SH "SEE ALSO"
scp
.SH BUGS
Please report to minjie_gu@126.com.

其中例如.TH\fB\fP等内容让文档可以产生加粗、标题等各种格式。

将这个文件存储为scpp.roff,之后输入这个命令就可以看到显示效果:

1
groff -Tascii -man scpp.roff | more

是不是就看到了目标文档的效果,如果你想看到特殊字符可以把他输出到文件:

1
groff -Tascii -man scpp.roff > scpp.1

roff格式的文件有大量的标记,如果你感兴趣,可以在这里扩展阅读。

md格式

markdown是目前非常常用的标记语言,也是纯文本编写有格式的文档,大量现在的博客都是用这个语言写成的。

相较而言,markdown的标记更少但也更简单,入门方便,也提供图片、表格等的插入。

本文档使用md格式就是这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
---
title: scpp
section: 1
date: 9 September 2019
---

# NAME
scpp - scp plus

# SYNOPSIS
**scpp** [push|pull] [scp options] source target

# DESCRIPTION
scpp simplifys file transformation between two machines
by extending basic command scp. scpp is a one-line command
that added to your bash profile. You need to specific
username ,server address and options before using.

## Options

**push** Push files to remote machine.

**pull** Pull files from remote machine.

**source** Specify the source file.

**target** Specify the target file.

# SEE ALSO
**scp**

# BUGS
Please report to minjie_gu@126.com.

将这个文件存储为scpp.md,之后输入这个命令就可以看到显示效果:

1
pandoc --standalone -f markdown -t man -o scpp_md.roff scpp.md | groff -Tascii -man scpp_md.roff | more

其中--standalone标签是为了给文档生成的独立的文件头。

开头的那些元数据提供了生成roff所需的一些必要内容。

为了让其与所有其他的man文档格式相同,我们先转为roff格式,再通过groff转化为富文本。

安装scpp的man文档

到了这一步,已经了解了三种不同的文件格式,也可以用最熟悉的markdown格式写man文档。

下一步就是将写好的内容进行安装,让帮助文件生效了。

不同的系统有不同的man手册目录,linux是/usr/man,osx是/usr/share/man

将文件生成在该目录就可以了。

1
groff -Tascii -man scpp.roff > /usr/man/man1/scpp.1

之后使用man scpp就能看到你自己写的man文档。

博客代码框设置换行

作者 Roy
2019年7月2日 20:02

博客里的代码框一直有个问题,每行的内容太长,代码框的宽度没法完整容纳一行。网上有过加横向滚动条、鼠标移上去就代码框自动加长这两种解决方案,但都不太美观。自动换行的方案符合大部分人的习惯,于是我研究了代码框的自动换行。本文设置了一个“行号和内容分开复制、代码自动换行并不影响自动高亮”的代码框,文中有可以用的代码以及用到属性的归纳。

基础的HTML代码

博客生成的代码框,格式经过简化就是如下的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<figure class="highlight javascript">
<table> <tbody> <tr>
<td class="gutter">
<pre>
<span class="line">0</span><br>
<span class="line">1</span><br>
<span class="line">2</span><br>
</pre>
</td>
<td class="code">
<pre>
<span class="line">"a "<span class="keyword">very</span>" very long line"</span><br>
<span class="line">a very very long line</span><br>
<span class="line">a very very long line</span><br>
</pre>
</td>
</tr> </tbody> </table>
</figure>

这些代码均为自动生成,也没有什么可以讨论的。代码高亮通过span实现;由于代码与行号生成在两个td中,所以复制代码的时候不会复制到行号。

换行的CSS设置

换行涉及到三个内容,具体实现的代码为:

1
2
3
4
figure .code span {
white-space: pre-wrap;
word-break: break-all;
}

其中分别的作用为:

  • white-space:设置元素中的空白符如何处理
  • overflow-wrap:设置完整单词的断开方式(未用到)
  • word-break:设置完整单词(包含汉语等的句子)的断开方式

white-space

空白符的处理简而言之是这样一张表:

属性多个换行空格和换行自动换行行末空格
normal保留一个保留一个移除
nowrap保留一个保留一个移除
pre保留保留保留
pre-wrap保留保留保留不换行
pre-line保留保留一个移除
break-spaces保留保留一个保留换行

我们的代码,对于多个换行需要全部保留,多个空格和换行需要全部保留,需要自动换行,行末空格保留但不需要因为空白字符换行。所以就white-space的属性可以选择pre-wrap

overflow-wrap

虽然空白符处理中我们设置了需要换行,但是过长的单词还是会导致行宽超出预设的宽度。

属性如何处理过长单词
normal保留
break-word断开

我们不希望因为有过长的单词导致出现横向滚动栏,所以这里可以选择break-word。但考虑到与word-break属性的作用重合,而且word-break属性更适合汉语的特别处理,所以就不再使用overflow-wrap

word-break

换行的设置也可以使用这个,这个属性可以将汉语整句视为一个单词不换行(直到有标点符号为止)。

属性英文处理中文处理
normal不断开单词断开句子
break-all断开单词断开句子
keep-all不断开单词不断开句子
break-word可能的话不断开单词断开句子

其中break-allbreak-word的区别为:若一行可以容纳一个单词但是无法容纳两个,break-all将断开第二个单词,break-word将把第二个单词放在第二行。

根据要求,我们就选用了break-all。这里其实和使用overflow-wrap: break-word是一样的效果。

换行的JS设置

自动换行之后就需要行号的位置根据代码的长度做相应变化,博客里本来有代码框自动调整宽度的代码,这里将行高的调整也加进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
+function($) {
'use strict';

var CodeBlockResizer = function(elem) {
this.$codeBlocks = $(elem);
};

CodeBlockResizer.prototype = {
run: function() {
var self = this;
self.resize();
$(window).smartresize(function() {
self.resize();
});
},

resize: function() {
var self = this;
self.$codeBlocks.each(function() {
var $gutter = $(this).find('.gutter');
var $code = $(this).find('.code');

var codePaddings = $code.width() - $code.innerWidth();
var width = $(this).outerWidth() - $gutter.outerWidth() + codePaddings;

$code.css('width', width);
$code.children('pre').css('width', width);

for (var i = 0, m = $gutter.find('span').length; i < m; i++) {
$gutter.find('span').eq(i).height(
$code.find('span.line')[i].offsetHeight);
}
});
}
};

$(document).ready(function() {
$.fn.hasHorizontalScrollBar = function() {
return this.get(0).scrollWidth > this.innerWidth();
};
var resizer = new CodeBlockResizer('figure.highlight');
resizer.run();
});
}(jQuery);

这里与行号位置相关的部分就是29-32行的内容,根据offsetHeight调整height

仅是这样还是无法更改行号的位置,原因在于目前显示代码使用的都是span元素。我们若需要通过height调整行号所占的高度,应将其显示方式改为inline-block,所以增加如下CSS:

1
2
3
4
figure.highlight pre > .line {
display: inline-block;
line-height: 18px;
}

其中如果不设置display: inline-blockline-heightheight也将不相同,可能出现的小数将导致不必要的麻烦。

到这里就完成了全部关于代码换行的设置。

畅言更名相关商标权的探究

作者 Roy
2019年6月10日 21:42

2019年6月6日,“畅言”官方公众号发布声明,“畅言”今后将会更名为“云评论”。出于好奇,我基于网络已经公开的信息,了解了一下这次商标侵权的内容。本文系本次事件的挖掘,并扩展联系了商标的相关内容。具体而言,文中简述了本次畅言商标侵权的背景,结合整理了相关的司法及商标局的记录,扩展罗列了相关法律法规。“畅言”变成“云评论”,只是改名的故事多没意思,如果发现是个商标侵权的故事,那就有很多值得一看的内容了。

这篇文章比较长,你可以挑着看,我也为此生成了目录。想听故事的话可以看“基本情况”这一节,想了解一些商标基础的话可以看“商标注册基础内容”这一节,只想看个结论的话“具体分析”这一节就够了。

基本情况

网上可以搜到相关的内容主要为:

  • 公众号声明内容
  • 两家公司背景
  • 声明后的变化
  • 诉讼开庭情况
  • 商标注册情况

公众号声明内容

2019年6月6日,“畅言”公众号(现已改名“云评论”)发布“关于侵犯科大讯飞股份有限公司注册商标权的致歉声明”。其中提到,“畅言”由于侵犯科大讯飞商标权,“畅言评论”将使用新名称“云评论”。

1
2
3
4
5
6
科大讯飞股份有限公司:

本公司因侵犯了科大讯飞股份有限公司持有的“畅言”注册商标权,致使科大讯飞股份有限公司之商标权受到损害,故本公司依据有关法律文书公开致歉,并承诺停止侵权,更换云评论官网(原搜狐畅言评论)(http://changyan.sohu.com 、http://changyan.kuaizhan.com)官网及管理后台出现的所有“畅言”的显示,该产品将使用新的名称“云评论”。同时呼吁业界尊重科大讯飞股份有限公司之合法权益,尊重商标权。

声明人:北京云站科技有限公司
2019年6月6日

两家公司背景

本声明涉及到两家公司,北京云站科技有限公司(下称“云站科技”)与科大讯飞股份有限公司(下称“科大讯飞”)。

云站科技旗下的“畅言”商标主要用于畅言评论系统,网络中可见的最早的记录为2012年11月27日,谷歌记录到了畅言评论发布了WordPress的插件(截图见这里)。而后畅言评论系统一直多见于各类论坛与博客,至今一直保持更新。由于目前国内各类评论插件都停止更新或不能使用,畅言评论系统已经成为很多人第三方评论系统的首选。

科大讯飞旗下的“畅言”商标主要用于畅言多媒体教学系统,网络中可见的最早记录为2012年3月29日,谷歌记录到了畅言智慧课堂教学系统的介绍文章(截图见这里)。而后网络上关于该服务的介绍较少,该情况可能因为该教学系统主要销售和宣传方式不是网络途径。目前科大讯飞旗下畅言相关的内容可在畅言云网站见到,更新了大量相关内容。

声明后的变化

当天,“云评论”官网上图标及各类提示都完成了替换。

不过云评论的二级域名还是保持了changyan.sohu.com。很大可能该域名不会弃用,“云评论”插件的载入也是依赖这一二级域名。一方面,这一二级域名显然并非商标权侵权行为。另一方面,启用另一域名yunpinglun.sohu.com作为一般用户的入口,让产品名称和二级域名统一并不会产生大量额外支出。

作为云评论的用户,我发现”云评论“广告栏位中竟然出现了侵权相对方科大讯飞的广告,我做了一个截图

作为用户不得不提的是,这两天的更新里针对广告部分做了优化,往常常规使用的CSS去广告已经失效。个人还是支持这一做法的,不付费用别人的产品,不屏蔽广告也是应有之义。不过如果广告实在太丑或者太打扰用户,通过一些手段改改大小或者换个地方合不合适呢?

诉讼开庭情况

通过公告可以看到商标权侵权的双方为,科大讯飞股份有限公司及北京云站科技有限公司。根据两公司的名字及案由,很容易的就能够搜索到公告信息(截图见这里)。

1
2
3
4
5
6
7
8
9
10
(2019)皖0191民初1247号 公告

我院定于2019年05月03日 09时00分在本院第十一法庭依法开庭审理被告北京云站科技有限公司与原告科大讯飞股份有限公司商标权权属、侵权纠纷一案。

案号:(2019)皖0191民初1247号
承办人:赵晨

特此公告

二〇一九年二月二十日

据此可以看到该案已经进入了司法程序,可能已经产生生效的法律文书。为此,我尝试搜索了中国裁判文书网(搜索结果见这里),截止到2019年6月10日没有找到相应的判决。考虑到调解书和部分判决书不上网以及判决书需要一定时间才会上网,这一结果也在情理之中。

不过不得不吐槽一下,查询过程当中裁判文书网大部分时间都处于“系统繁忙,请您稍后再试”的状态,搜索实在难以完成。当我看到有一个注册界面的时候,我朴素的以为由于我不是注册用户,搜索享受着爬虫待遇。结果不仅注册无法完成,“用户名查重失败:Index was outside the bounds of the array.”;而且找朋友借了一个账号后搜索速度完全没有好转。

通过开庭的信息,可以确定的是云站科技与科大讯飞之间确实存在商标权权属争议及侵权纠纷,该纠纷最后以云站科技停止使用“畅言”商标结束。

商标注册情况

国家知识产权局商标局有下设中国商标网,通过其中的综合搜索,搜索商标名称为“畅言”的商标,可以找到“畅言”相关的34个商标。

其中本次的双方注册情况为:

  • 科大讯飞股份有限公司于2000年01月24日申请了名称为“畅言”的9类商标
  • 科大讯飞股份有限公司于2008年05月14日至2011年05月31日申请了名称为“畅言”的9、16、28、41、42类商标
  • 科大讯飞股份有限公司于2018年12月05日申请了名称为“畅言”的9、16、41、42类商标
  • 云站科技科技有限公司于2019年04月09日申请了名称为“畅言云评”的9、38、42类商标

具体内容我做了一张表,表的内容见这里

商标注册内容

这里涉及到几类商标,具体的内容有《类似商品和服务区分表》具体作了解释,简单的来说:

  • 9:
    • 0901: 电子计算机机器外部设备
    • 0907: 通讯导航设备
    • 0908: 音响设备
    • 0923: 电影片,已曝光材料
  • 38:
    • 3801: 进行播放无线电或电视节目的服务
    • 3802: 通讯服务
  • 42:
    • 4220: 计算机编程及相关服务

考虑到2019年2月20日已作出开庭公告,2019年4月9日云站科技申请商标的行为在后,可以推测云站科技认为其在9、38、42类享有商标权,具体的小类为上述几类。

其中几个小类的商标权科大讯飞并未申请,分别为0907、0923、3801、3802、4220。科大讯飞有在先申请的为0901与0908。

就商品/服务项目而言,类似群重合的部分注册情况为:

  • 云站科技:
    • 0901:可下载的音乐文件、笔记本电脑、可下载的影像文件、计算机软件(已录制)、计算机程序(可下载软件)、电子出版物(可下载)、可下载的计算机游戏软件
    • 0908:便携式媒体播放器
  • 科大讯飞:
    • 0901:计算机存储装置、计算机、磁盘、可下载的影像文件、可下载的计算机应用软件、可下载的手机应用软件、电子出版物(可下载)、计算机软件、计算机程序、读出器、数据处理设备、条码读出器、文字处理机、信息处理机
    • 0908:录音装置、带有图书的电子发声装置、扬声器音箱、个人用立体声装置

云站科技注册商标

中国商标网的搜索结果中可以看到云站科技注册的商标,通过网上简单的字体比对可以发现云站科技注册的“畅言云评”图标构成十分简单。图标为800x800的图片,内容为“畅言云评”四个字,字体为宋体,没有特别的设计或装饰。

我将“畅言云评”的商标与宋体的这四个字做了一个对比,对比图见这里

其中宋体是一个有意思的字体,宋体一直给人一种默认自带的感觉,但其实他的著作权归属于北京中易中标电子信息技术有限公司(下称“中易中标”)。我们常见的微软自带宋体背后,中易中标与微软签订过《字体许可协议》,微软获得了中易中标的许可。但由于Windows 98及之后的系统不属于授权范围,中易中标于2007年起诉微软,产生了(2007)一中民初字第5362号的著作权纠纷,具体内容即为宋体及黑体的著作权侵权。

可见宋体的商用需要获得中易中标的授权,未授权的使用属于侵犯著作权的行为。不过中易中标可见的诉讼记录中除了微软的诉讼未发现对任何一家公司的判决,可能其怠于行使民事权利甚至默认该类侵权行为,当然考虑到该公司背景也完全可能通过其他途径实现民事权利。

商标注册相关内容

注册流程

商标注册有一系列流程,具体而言包含,向商标局提交申请书、形式审查、实质审查、初步审定公告、注册公告、注册。每一步骤的期限以及具体要求法律都有明确的规定,整体流程完成后即完成注册。

整个过程还是很复杂的,有兴趣的这里提供一张流程图(点击这里查看)。

可以看到整个注册过程就是审查与公告,经过了法定的公告期没有成立的异议则申请人可以获得申请商标的商标权。

商标大类与小类

本文中提到了商标的大类与小类,标准来讲,商标的大类即国际分类,商标的小类即类似群。从上面的申请表国标网显示的内容可能会发现,商标申请仅需要提供类别和商品/服务项目,不需要填写类似群。商标/服务项目是类似群更下层的分类,以笔记本电脑为例,其大类(国际分类)为9,小类(类似群)为0901,商标/服务项目是笔记本电脑(090103)。

商标注册与保护的联系

那么商标权是怎么保护的呢,大类相同就可保护,还是需要商标/服务项目相同才可保护?结论是很有意思的,保护和分类并没有必然的关系。

根据《最高人民法院知识产权案件年度报告(2011)》,商标保护的认定不能根据物理属性确认,而应判断是否能共存。若侵权商标与原商标无法共存,则应认定侵权商标涉及的商品也在原商标的保护范围内。“最高人民法院审查认为:商标法设置商品类似关系,是因为商标主要是按商品类别进行注册、管理和保护。在商标授权确权和侵权判定过程中,进行商标法意义上相关商品是否类似的判断,并非作相关商品物理属性的比较,而是主要考虑商标能否共存或者决定商标保护范围的大小。”

为此,最高法以鞋商标侵权服装商标为例,具体阐述了该问题。“避免来源混淆是商品类似关系判断时要坚持的一项基本原则。本案中,争议商标指定使用的商品为鞋和靴,引证商标核定使用的商品是服装等。虽然两者在具体的原料、用途等方面具有一些差别,但是两者的消费对象是相同的,而且在目前的商业环境下,一个厂商同时生产服装和鞋类产品,服装和鞋通过同一渠道销售,比如同一专卖店、专柜销售的情形较为多见。同时,争议商标与引证商标中的“鸟图形”虽然在细部上略有差异,但两者基本形态相同,且根据查明的事实,引证商标通过使用具有较高的知名度。在这种情况下,如果两商标在服装和鞋类商品上共存,容易使相关公众认为两商品是同一主体提供的,或者其提供者之间存在特定联系。因此,争议商标与引证商标构成类似商品上的近似商标。”

关于《类似商品和服务区分表》,也就是其中大类、小类、商标/服务项目的分类,最高法院给出了这样一个态度。“最高人民法院认为,《区分表》可以作为判断类似商品或者服务的参考,但不能机械、简单地以《区分表》为依据或标准,而应当更多地考虑实际因素,结合个案的情况进行认定。”

所以一般同类的商品属于侵权,但实际上也完全可能存在跨类的侵权,例如最高法提到的鞋与服装的跨类侵权。

商标侵权相关法律

《中华人民共和国商标法》

第十三条 为相关公众所熟知的商标,持有人认为其权利受到侵害时,可以依照本法规定请求驰名商标保护。

第四十九条 注册商标成为其核定使用的商品的通用名称或者没有正当理由连续三年不使用的,任何单位或者个人可以向商标局申请撤销该注册商标。商标局应当自收到申请之日起九个月内做出决定。有特殊情况需要延长的,经国务院工商行政管理部门批准,可以延长三个月。

第五十七条 有下列行为之一的,均属侵犯注册商标专用权:
  (一)未经商标注册人的许可,在同一种商品上使用与其注册商标相同的商标的;
  (二)未经商标注册人的许可,在同一种商品上使用与其注册商标近似的商标,或者在类似商品上使用与其注册商标相同或者近似的商标,容易导致混淆的;
  (三)销售侵犯注册商标专用权的商品的;
  (四)伪造、擅自制造他人注册商标标识或者销售伪造、擅自制造的注册商标标识的;
  (五)未经商标注册人同意,更换其注册商标并将该更换商标的商品又投入市场的;
  (六)故意为侵犯他人商标专用权行为提供便利条件,帮助他人实施侵犯商标专用权行为的;
  (七)给他人的注册商标专用权造成其他损害的。

第五十九条 商标注册人申请商标注册前,他人已经在同一种商品或者类似商品上先于商标注册人使用与注册商标相同或者近似并有一定影响的商标的,注册商标专用权人无权禁止该使用人在原使用范围内继续使用该商标,但可以要求其附加适当区别标识。

第六十三条 侵犯商标专用权的赔偿数额,按照权利人因被侵权所受到的实际损失确定;实际损失难以确定的,可以按照侵权人因侵权所获得的利益确定;权利人的损失或者侵权人获得的利益难以确定的,参照该商标许可使用费的倍数合理确定。对恶意侵犯商标专用权,情节严重的,可以在按照上述方法确定数额的一倍以上五倍以下确定赔偿数额。赔偿数额应当包括权利人为制止侵权行为所支付的合理开支。

《最高人民法院关于审理商标民事纠纷案件适用法律若干问题的解释》

第十一条 商标法第五十二条第(一)项规定的类似商品,是指在功能、用途、生产部门、销售渠道、消费对象等方面相同,或者相关公众一般认为其存在特定联系、容易造成混淆的商品。
类似服务,是指在服务的目的、内容、方式、对象等方面相同,或者相关公众一般认为存在特定联系、容易造成混淆的服务。
商品与服务类似,是指商品和服务之间存在特定联系,容易使相关公众混淆。

具体分析

是否实际侵权

本案双方的商标不同,属于近似商标,应适用《商标法》第五十七条第二款认定侵权,“未经商标注册人的许可,在同一种商品上使用与其注册商标近似的商标,或者在类似商品上使用与其注册商标相同或者近似的商标,容易导致混淆的”。也就是说,一方面,侵权的前提是商品属于同一种类。另一方面,使用近似商标并不一定承担侵权责任,还必须判断是否会使消费者产生混淆。

云站科技的“畅言评论”自2012年起提供的服务均为社会化评论系统,科大讯飞的“畅言教育”自2012年起提供的服务均为教育辅助系统。“畅言评论”为评论插件,是完整网页或者应用的可选组成部分,“畅言教育”为教学辅助应用,是完整的应用,难以认定为同一种商品。侵权的前提无法达到,故难以认定为侵权。从混淆的角度来说,“畅言评论”提供的是评论服务,“畅言教育”提供的是教育资源与数据,内容上无法构成混淆。“畅言评论”的消费者是需要评论功能的开发者,“畅言教育”的消费者是需要教育资源与数据的教育机构或开发者,目标消费者人群不同,难以产生混淆的效果。

故从前提或混淆的效果,本案均难以认定为商标权侵权。

可能的结果

关于更名的结果已经不用说了,云站科技选择不再使用“畅言”商标。

就用户的吸引与流失,老用户而言,可以想象没什么影响,改名后第一次点进后台可能会有些惊讶。而新用户,身边使用过”畅言“评论插件的朋友,大都是通过旧的文章介绍了解到他。如果无法再通过搜索”畅言“找到该插件,势必会影响新用户的产生。

好在目前,我通过常用的几个搜索引擎进行了搜索,云站科技的网站目前仍旧排在“畅言”搜索结果的首位。但值得注意的是,”畅言“教育平台的搜索结果,虽在前两位的”畅言“评论系统后面,但也占据了后面的四个到五个位置。随着更名后的时间推移以及科大讯飞可能的加大”畅言“教育平台的宣传力度,也许不久之后”畅言“评论系统将被挤出搜索的第一页,从而无法通过积累的“畅言”口碑获得用户。

另一个值得一提的是,科大讯飞在云站科技投放了广告,科大讯飞在使用云评论插件的广告栏位。我也好奇这个广告栏位的使用,是云站科技给出的补偿,还是科大讯飞付钱买商标的方式呢?单就从云站科技实际侵权的可能性较小的角度来看,这一点云站科技的常法律师出具的法律意见书中一定会提到,很大可能使用广告栏会是付费的。

国内云评论服务的提供商越来越少,起码“云评论”还是个完备可用的选择。“云评论”评论系统这次更名,失去了“畅言”的商标,避免了一个麻烦,可能获得了一笔不知数额的广告费。总有一种把自家牌匾卖了换钱的感觉,揭得开锅谁卖牌匾,和他死磕到底呗。也不知道现在“云评论”的经营情况如何,还能不能继续下去。

❌
❌